Ergänze Auftragsbearbeitung, Disposition, Stammdaten, Kunden- und Transporteurportale, Lagerverwaltung, Abrechnung, Exporte und Statistik. Füge Kontoeinstellungen, Mitteilungen, Automatikdienste und Betriebsstatus sowie die zugehörigen Tests und PHP-Abgleichsdokumentation hinzu. Vergrößere die persönlichen Auftragslisten auf einheitliche Höhen und richte Statusanzeigen sowie die Ranking-Vorschau-Steuerelemente bündig aus. Validierung: 44 Frontendtests und Produktionsbuild erfolgreich. Backend funktional unverändert gegenüber dem vollständigen Lauf mit 1.624 erfolgreichen und drei übersprungenen Tests. Layout im Browser und lokale Bereitstellung geprüft.
19 lines
2.7 KiB
TypeScript
19 lines
2.7 KiB
TypeScript
import {useEffect,useState,type CSSProperties} from 'react';
|
|
import {request} from './api';
|
|
import {Modal} from './FormElements';
|
|
export type DispositionCard={id:number;lines:string[];details:string[];zipcode:string;verticalId:boolean;colour:string;category:string};
|
|
export function useDispositionCards(path:string|null,board:unknown):{cards:Record<number,DispositionCard>;error:string;denied:boolean;hideHours:boolean}{
|
|
const [result,setResult]=useState<{path:string;board:unknown;cards:Record<number,DispositionCard>;error:string;denied:boolean;hideHours:boolean}|null>(null);
|
|
useEffect(()=>{const c=new AbortController();if(!path)return;request<{cards:DispositionCard[];hideHours?:boolean}>(path,{signal:c.signal}).then(v=>{if(!c.signal.aborted)setResult({path,board,cards:Object.fromEntries(v.cards.map(card=>[card.id,card])),error:'',denied:false,hideHours:v.hideHours===true});}).catch(e=>{if(!c.signal.aborted)setResult({path,board,cards:{},error:e.message,denied:e.status===403,hideHours:false});});return()=>c.abort();},[path,board]);
|
|
return result&&result.board===board&&result.path===path?result:{cards:{},error:'',denied:false,hideHours:false};
|
|
}
|
|
export function cardStyle(card?:DispositionCard):CSSProperties{
|
|
if(!card)return {};let hex=card.colour.slice(1);if(hex.length===3)hex=hex.split('').map(c=>c+c).join('');
|
|
const values=[0,2,4].map(i=>{const v=parseInt(hex.slice(i,i+2),16)/255;return v<=.04045?v/12.92:((v+.055)/1.055)**2.4;});
|
|
const luminance=values[0]*.2126+values[1]*.7152+values[2]*.0722;
|
|
return {backgroundColor:card.colour,color:(luminance+.05)/.05>=1.05/(luminance+.05)?'#000':'#fff'};
|
|
}
|
|
export function cardDescription(card?:DispositionCard){return card?[card.category,...card.lines,...card.details].join('\n'):'';}
|
|
export function DispositionCardText({job,card}:{job:number;card?:DispositionCard}){return <>{card?.verticalId&&<span className="fds-card-id-vertical" aria-hidden="true">{job}</span>}{card?.zipcode&&<span className="fds-card-zip" aria-label={`Postleitzahl ${card.zipcode}`}>{card.zipcode}</span>}<span className="fds-card-body"><strong>#{job}{card&&` · ${card.category}`}</strong>{card?.lines.map((line,i)=><span className="fds-card-line" key={i}>{line||'\u00a0'}</span>)}</span></>;}
|
|
export function DispositionCardDetails({card,onClose}:{card:DispositionCard;onClose:()=>void}){return <Modal title={`Auftrag #${card.id} · ${card.category}`} onClose={onClose}><div className="modal-body"><div className="fds-card-detail">{card.details.map((line,i)=><p key={i}>{line}</p>)}</div><hr/>{card.lines.filter(line=>line&&!card.details.includes(line)&&!card.details.includes("Zusage: "+line)).map((line,i)=><p key={i}>{line}</p>)}</div><div className="modal-footer"><button className="button" onClick={onClose}>Schließen</button></div></Modal>;}
|