Portal-dias-rota/src/app/dias-rota/pagina.tsx

289 lines
9.0 KiB
TypeScript
Raw Normal View History

'use client';
import {
CalendarBody,
CalendarDate,
CalendarDatePagination,
CalendarDatePicker,
CalendarHeader,
CalendarMonthPicker,
CalendarProvider,
CalendarYearPicker,
type Feature,
} from '@/components/kibo-ui/calendar';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import {
changeBaldinhoDaysSale,
changeBaldinhoDeliverySize,
changeNoDelivery,
} from '../action/update-baldinho';
import { type GetBladinhoResponse } from '../data-access/calendario/get-bladinho';
interface PaginaProps {
bladinhoData: GetBladinhoResponse[];
}
// Componente customizado para exibir informações em múltiplas linhas
const CustomCalendarItem = ({ feature }: { feature: Feature }) => {
const lines = feature.name.split('\n');
return (
<div className="flex items-start gap-2">
<div
className="h-2 w-2 shrink-0 rounded-full mt-1"
style={{
backgroundColor: feature.status.color,
}}
/>
<div className="flex flex-col gap-0.5 min-w-0">
{lines.map((line, index) => (
<span key={index} className="text-xs leading-tight break-words">
{line}
</span>
))}
</div>
</div>
);
};
const Pagina = ({ bladinhoData }: PaginaProps) => {
// Estado para controlar a abertura do AlertDialog
const [isDialogOpen, setIsDialogOpen] = useState(false);
// Estado para armazenar a data e o novo valor de delivery quando o usuário clicar
const [pendingUpdate, setPendingUpdate] = useState<{
date: Date;
delivery: boolean;
} | null>(null);
// Transforma os dados do bladinho em features do calendário
const [valorBaldinho, setValorBaldinho] = useState<number | undefined>(
bladinhoData[0].deliverySize ?? 0
);
const [diasBaldinho, setDiasBaldinho] = useState<number | undefined>(
bladinhoData[0].daysSale ?? 0
);
const [isLoadingDaysSale, setIsLoadingDaysSale] = useState(false);
const [isLoadingDeliverySize, setIsLoadingDeliverySize] = useState(false);
const features = useMemo<Feature[]>(() => {
return bladinhoData.map((item) => {
const dateDelivery = new Date(item.dateDelivery);
const additionalInfo = [
`Capacidade: ${item.deliverySize}`,
`Peso Venda: ${item.saleWeight}`,
`Disponível: ${item.avaliableDelivery}`,
].join('\n');
return {
id: item.id.toString(),
name: additionalInfo,
startAt: dateDelivery,
endAt: dateDelivery,
status: {
id: item.id.toString(),
name: item.delivery ? 'Delivery' : 'No Delivery',
color: item.delivery ? '#6B7280' : '#EF4444',
},
isSelected: !item.delivery,
delivery: item.delivery,
} as Feature & { delivery: boolean };
});
}, [bladinhoData]);
const selectedFeatures = useMemo(() => features, [features]);
const earliestYear = useMemo(
() =>
selectedFeatures
.map((feature) => feature.startAt.getFullYear())
.sort()
.at(0) ?? new Date().getFullYear(),
[selectedFeatures]
);
const latestYear = useMemo(
() =>
selectedFeatures
.map((feature) => feature.endAt.getFullYear())
.sort()
.at(-1) ?? new Date().getFullYear(),
[selectedFeatures]
);
const handleDateClick = (date: Date, dayFeatures?: Feature[]) => {
// Encontra o feature correspondente à data clicada
const featureForDate = dayFeatures?.find((feature) => {
const featureDate = new Date(feature.startAt);
return (
featureDate.getDate() === date.getDate() &&
featureDate.getMonth() === date.getMonth() &&
featureDate.getFullYear() === date.getFullYear()
);
});
let newDelivery: boolean;
if (featureForDate) {
// Extrai o isSelected atual do feature
const currentIsSelected = featureForDate.isSelected ?? false;
// Inverte o valor para obter o novo delivery (!isSelected)
newDelivery = !currentIsSelected;
} else {
// Se não há feature para esta data, cria um novo com delivery = true
newDelivery = true;
}
// Armazena os dados e abre o dialog
setPendingUpdate({ date, delivery: newDelivery });
setIsDialogOpen(true);
};
const handleConfirmUpdate = async () => {
if (pendingUpdate) {
// Executa o update apenas quando o usuário confirmar
await changeNoDelivery(pendingUpdate.date, pendingUpdate.delivery);
// Fecha o dialog e limpa o estado
setIsDialogOpen(false);
setPendingUpdate(null);
}
};
const handleCancelUpdate = () => {
// Apenas fecha o dialog e limpa o estado, sem fazer update
setIsDialogOpen(false);
setPendingUpdate(null);
};
const handleBaldinhoDaysSaleChange = async (daysSale: number) => {
if ((daysSale ?? 0) <= 0) {
toast.error('O valor deve ser maior que zero');
return;
}
setDiasBaldinho(daysSale);
setIsLoadingDaysSale(true);
const toastId = toast.loading('Atualizando dias baldinho...');
try {
await changeBaldinhoDaysSale(daysSale);
toast.success(`Dias baldinho atualizado para ${daysSale}`, {
id: toastId,
});
} catch (error) {
toast.error('Erro ao atualizar dias baldinho', { id: toastId });
} finally {
setIsLoadingDaysSale(false);
}
};
const handleBaldinhoDeliverySizeChange = async (deliverySize: number) => {
if ((deliverySize ?? 0) <= 0) {
toast.error('O valor deve ser maior que zero');
return;
}
setValorBaldinho(deliverySize);
setIsLoadingDeliverySize(true);
const toastId = toast.loading('Atualizando capacidade baldinho...');
try {
await changeBaldinhoDeliverySize(deliverySize);
toast.success(`Capacidade baldinho atualizada para ${deliverySize}`, {
id: toastId,
});
} catch (error) {
toast.error('Erro ao atualizar capacidade baldinho', { id: toastId });
} finally {
setIsLoadingDeliverySize(false);
}
};
return (
<AlertDialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<CalendarProvider>
<CalendarDate>
<CalendarDatePicker>
<div className="flex flex-row gap-2">
<CalendarMonthPicker />
<CalendarYearPicker end={latestYear} start={earliestYear} />
<div className="flex flex-row gap-2 items-center">
<Label className="text-sm min-w-32">Capacidade</Label>
<Input
type="number"
value={valorBaldinho}
onChange={(e) => setValorBaldinho(Number(e.target.value))}
/>
<Button
type="button"
onClick={() =>
handleBaldinhoDeliverySizeChange(valorBaldinho ?? 0)
}
disabled={isLoadingDeliverySize}
>
{isLoadingDeliverySize ? 'Salvando...' : 'Salvar'}
</Button>
</div>
<div className="flex flex-row gap-2 items-center">
<Label className="text-sm min-w-32">Dias úteis</Label>
<Input
type="number"
value={diasBaldinho}
onChange={(e) => setDiasBaldinho(Number(e.target.value))}
/>
<Button
type="button"
onClick={() =>
handleBaldinhoDaysSaleChange(diasBaldinho ?? 0)
}
disabled={isLoadingDaysSale}
>
{isLoadingDaysSale ? 'Salvando...' : 'Salvar'}
</Button>
</div>
</div>
</CalendarDatePicker>
<CalendarDatePagination />
</CalendarDate>
<CalendarHeader />
<CalendarBody features={selectedFeatures} onDateClick={handleDateClick}>
{({ feature }) => (
<CustomCalendarItem feature={feature} key={feature.id} />
)}
</CalendarBody>
</CalendarProvider>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Confirmar alteração</AlertDialogTitle>
<AlertDialogDescription>
Tem certeza que deseja alterar o status de entrega para esta data?
Esta ação não pode ser desfeita.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={handleCancelUpdate}>
Cancelar
</AlertDialogCancel>
<AlertDialogAction onClick={handleConfirmUpdate}>
Confirmar
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};
export default Pagina;