Merge branch 'backup/visual-adjustments' into fix/visual-adjustments
This commit is contained in:
commit
ba41468f37
@ -111,8 +111,11 @@ export default function ConsultasPage() {
|
||||
const baseDate = scheduledBase ? new Date(scheduledBase) : new Date();
|
||||
const duration = appointment.duration_minutes ?? appointment.duration ?? 30;
|
||||
|
||||
// compute start and end times (HH:MM)
|
||||
const appointmentDateStr = baseDate.toISOString().split("T")[0];
|
||||
// compute start and end times (HH:MM) and date using local time to avoid timezone issues
|
||||
const year = baseDate.getFullYear();
|
||||
const month = String(baseDate.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(baseDate.getDate()).padStart(2, '0');
|
||||
const appointmentDateStr = `${year}-${month}-${day}`;
|
||||
const startTime = `${String(baseDate.getHours()).padStart(2, '0')}:${String(baseDate.getMinutes()).padStart(2, '0')}`;
|
||||
const endDate = new Date(baseDate.getTime() + duration * 60000);
|
||||
const endTime = `${String(endDate.getHours()).padStart(2, '0')}:${String(endDate.getMinutes()).padStart(2, '0')}`;
|
||||
@ -811,7 +814,7 @@ export default function ConsultasPage() {
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label className="text-right">Tipo</Label>
|
||||
<span className="col-span-3">{capitalize(viewingAppointment?.type || "")}</span>
|
||||
<span className="col-span-3">{capitalize(viewingAppointment?.appointment_type || viewingAppointment?.type || "")}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label className="text-right">Observações</Label>
|
||||
|
||||
@ -131,6 +131,7 @@ export default function DoutoresPage() {
|
||||
const [availabilityOpenFor, setAvailabilityOpenFor] = useState<Medico | null>(null);
|
||||
const [availabilityViewingFor, setAvailabilityViewingFor] = useState<Medico | null>(null);
|
||||
const [availabilities, setAvailabilities] = useState<DoctorAvailability[]>([]);
|
||||
const [availabilitiesForCreate, setAvailabilitiesForCreate] = useState<DoctorAvailability[]>([]);
|
||||
const [availLoading, setAvailLoading] = useState(false);
|
||||
const [editingAvailability, setEditingAvailability] = useState<DoctorAvailability | null>(null);
|
||||
const [exceptions, setExceptions] = useState<DoctorException[]>([]);
|
||||
@ -633,7 +634,17 @@ export default function DoutoresPage() {
|
||||
Ver pacientes atribuídos
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem onClick={() => setAvailabilityOpenFor(doctor)}>
|
||||
<DropdownMenuItem onClick={async () => {
|
||||
try {
|
||||
const list = await listarDisponibilidades({ doctorId: doctor.id, active: true });
|
||||
setAvailabilitiesForCreate(list || []);
|
||||
setAvailabilityOpenFor(doctor);
|
||||
} catch (e) {
|
||||
console.warn('Erro ao carregar disponibilidades:', e);
|
||||
setAvailabilitiesForCreate([]);
|
||||
setAvailabilityOpenFor(doctor);
|
||||
}
|
||||
}}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
Criar disponibilidade
|
||||
</DropdownMenuItem>
|
||||
@ -869,6 +880,7 @@ export default function DoutoresPage() {
|
||||
open={!!availabilityOpenFor}
|
||||
onOpenChange={(open) => { if (!open) setAvailabilityOpenFor(null); }}
|
||||
doctorId={availabilityOpenFor?.id}
|
||||
existingAvailabilities={availabilitiesForCreate}
|
||||
onSaved={(saved) => { console.log('Disponibilidade salva', saved); setAvailabilityOpenFor(null); /* optionally reload list */ reloadAvailabilities(availabilityOpenFor?.id); }}
|
||||
/>
|
||||
)}
|
||||
@ -890,6 +902,7 @@ export default function DoutoresPage() {
|
||||
doctorId={editingAvailability?.doctor_id ?? availabilityViewingFor?.id}
|
||||
availability={editingAvailability}
|
||||
mode="edit"
|
||||
existingAvailabilities={availabilities}
|
||||
onSaved={(saved) => { console.log('Disponibilidade atualizada', saved); setEditingAvailability(null); reloadAvailabilities(editingAvailability?.doctor_id ?? availabilityViewingFor?.id); }}
|
||||
/>
|
||||
)}
|
||||
@ -910,14 +923,35 @@ export default function DoutoresPage() {
|
||||
<div>Carregando disponibilidades…</div>
|
||||
) : availabilities && availabilities.length ? (
|
||||
<div className="space-y-2">
|
||||
{availabilities.map((a) => (
|
||||
{availabilities
|
||||
.sort((a, b) => {
|
||||
// Define a ordem dos dias da semana (Segunda a Domingo)
|
||||
const weekdayOrder: Record<string, number> = {
|
||||
'segunda': 1, 'segunda-feira': 1, 'mon': 1, 'monday': 1, '1': 1,
|
||||
'terca': 2, 'terça': 2, 'terça-feira': 2, 'tue': 2, 'tuesday': 2, '2': 2,
|
||||
'quarta': 3, 'quarta-feira': 3, 'wed': 3, 'wednesday': 3, '3': 3,
|
||||
'quinta': 4, 'quinta-feira': 4, 'thu': 4, 'thursday': 4, '4': 4,
|
||||
'sexta': 5, 'sexta-feira': 5, 'fri': 5, 'friday': 5, '5': 5,
|
||||
'sabado': 6, 'sábado': 6, 'sat': 6, 'saturday': 6, '6': 6,
|
||||
'domingo': 7, 'dom': 7, 'sun': 7, 'sunday': 7, '0': 7, '7': 7
|
||||
};
|
||||
|
||||
const getWeekdayOrder = (weekday: any) => {
|
||||
if (typeof weekday === 'number') return weekday === 0 ? 7 : weekday;
|
||||
const normalized = String(weekday).toLowerCase().normalize('NFD').replace(/\p{Diacritic}/gu, '');
|
||||
return weekdayOrder[normalized] || 999;
|
||||
};
|
||||
|
||||
return getWeekdayOrder(a.weekday) - getWeekdayOrder(b.weekday);
|
||||
})
|
||||
.map((a) => (
|
||||
<div key={String(a.id)} className="p-2 border rounded flex justify-between items-start">
|
||||
<div>
|
||||
<div className="font-medium">{translateWeekday(a.weekday)} • {a.start_time} — {a.end_time}</div>
|
||||
<div className="text-xs text-muted-foreground">Duração: {a.slot_minutes} min • Tipo: {a.appointment_type || '—'} • {a.active ? 'Ativa' : 'Inativa'}</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" variant="outline" onClick={() => setEditingAvailability(a)}>Editar</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => setEditingAvailability(a)} className="hover:bg-muted hover:text-foreground">Editar</Button>
|
||||
<Button size="sm" variant="destructive" onClick={async () => {
|
||||
if (!confirm('Excluir esta disponibilidade?')) return;
|
||||
try {
|
||||
|
||||
@ -175,8 +175,17 @@ export default function EditarLaudoPage() {
|
||||
mostrarAssinatura: !r.hide_signature,
|
||||
});
|
||||
|
||||
// Preencher conteúdo
|
||||
const contentHtml = r.content_html || r.conteudo_html || '';
|
||||
// Preencher conteúdo - verificar todos os possíveis nomes de campo
|
||||
const contentHtml = r.content_html || r.conteudo_html || r.contentHtml || r.conteudo || r.content || '';
|
||||
console.log('[EditarLaudoPage] Loading content - report:', r);
|
||||
console.log('[EditarLaudoPage] Content fields check:', {
|
||||
content_html: r.content_html,
|
||||
conteudo_html: r.conteudo_html,
|
||||
contentHtml: r.contentHtml,
|
||||
conteudo: r.conteudo,
|
||||
content: r.content,
|
||||
finalContent: contentHtml
|
||||
});
|
||||
|
||||
// Verificar se existe rascunho salvo no localStorage
|
||||
let finalContent = contentHtml;
|
||||
@ -206,10 +215,9 @@ export default function EditarLaudoPage() {
|
||||
|
||||
setCampos(finalCampos);
|
||||
setContent(finalContent);
|
||||
console.log('[EditarLaudoPage] Setting content state with length:', finalContent.length);
|
||||
|
||||
if (editorRef.current) {
|
||||
editorRef.current.innerHTML = finalContent;
|
||||
}
|
||||
// O innerHTML será setado no useEffect separado abaixo
|
||||
} catch (err) {
|
||||
console.warn('Erro ao carregar laudo:', err);
|
||||
toast({
|
||||
@ -224,6 +232,14 @@ export default function EditarLaudoPage() {
|
||||
fetchLaudo();
|
||||
}, [laudoId, token, toast]);
|
||||
|
||||
// UseEffect separado para injetar o conteúdo no editor quando estiver pronto
|
||||
useEffect(() => {
|
||||
if (content && editorRef.current && !loading) {
|
||||
console.log('[EditarLaudoPage] Injecting content into editor, length:', content.length);
|
||||
editorRef.current.innerHTML = content;
|
||||
}
|
||||
}, [content, loading]);
|
||||
|
||||
// Formatação com contenteditable
|
||||
const applyFormat = (command: string, value?: string) => {
|
||||
document.execCommand(command, false, value || undefined);
|
||||
|
||||
@ -1865,7 +1865,15 @@ const ProfissionalPage = () => {
|
||||
function LaudoEditor({ pacientes, laudo, onClose, isNewLaudo, preSelectedPatient, createNewReport, updateExistingReport, reloadReports, onSaved }: { pacientes?: any[]; laudo?: any; onClose: () => void; isNewLaudo?: boolean; preSelectedPatient?: any; createNewReport?: (data: any) => Promise<any>; updateExistingReport?: (id: string, data: any) => Promise<any>; reloadReports?: () => Promise<void>; onSaved?: (r:any) => void }) {
|
||||
const { toast } = useToast();
|
||||
const [activeTab, setActiveTab] = useState("editor");
|
||||
const [content, setContent] = useState(laudo?.conteudo || "");
|
||||
// Initialize content checking all possible field names
|
||||
const initialContent = laudo?.conteudo ?? laudo?.content_html ?? laudo?.contentHtml ?? laudo?.content ?? "";
|
||||
console.log('[LaudoEditor] Initializing content - laudo:', laudo, 'initialContent length:', initialContent?.length, 'fields:', {
|
||||
conteudo: laudo?.conteudo,
|
||||
content_html: laudo?.content_html,
|
||||
contentHtml: laudo?.contentHtml,
|
||||
content: laudo?.content
|
||||
});
|
||||
const [content, setContent] = useState(initialContent);
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
const [pacienteSelecionado, setPacienteSelecionado] = useState<any>(preSelectedPatient || null);
|
||||
const [listaPacientes, setListaPacientes] = useState<any[]>([]);
|
||||
@ -1952,8 +1960,10 @@ const ProfissionalPage = () => {
|
||||
// Carregar dados do laudo existente quando disponível (mais robusto: suporta vários nomes de campo)
|
||||
useEffect(() => {
|
||||
if (laudo && !isNewLaudo) {
|
||||
console.log('[LaudoEditor useEffect] Loading existing laudo data:', laudo);
|
||||
// Conteúdo: aceita 'conteudo', 'content_html', 'contentHtml', 'content'
|
||||
const contentValue = laudo.conteudo ?? laudo.content_html ?? laudo.contentHtml ?? laudo.content ?? "";
|
||||
console.log('[LaudoEditor useEffect] Content value length:', contentValue?.length, 'Setting content...');
|
||||
setContent(contentValue);
|
||||
|
||||
// Campos: use vários fallbacks
|
||||
|
||||
@ -18,9 +18,11 @@ export interface AvailabilityFormProps {
|
||||
// when editing, pass the existing availability and set mode to 'edit'
|
||||
availability?: DoctorAvailability | null
|
||||
mode?: 'create' | 'edit'
|
||||
// existing availabilities to prevent duplicate weekday selection
|
||||
existingAvailabilities?: DoctorAvailability[]
|
||||
}
|
||||
|
||||
export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved, availability = null, mode = 'create' }: AvailabilityFormProps) {
|
||||
export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved, availability = null, mode = 'create', existingAvailabilities = [] }: AvailabilityFormProps) {
|
||||
const [weekday, setWeekday] = useState<string>('segunda')
|
||||
const [startTime, setStartTime] = useState<string>('09:00')
|
||||
const [endTime, setEndTime] = useState<string>('17:00')
|
||||
@ -31,6 +33,26 @@ export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved,
|
||||
const { toast } = useToast()
|
||||
const [blockedException, setBlockedException] = useState<null | { date: string; reason?: string; times?: string }>(null)
|
||||
|
||||
// Normalize weekday to standard format for comparison
|
||||
const normalizeWeekdayForComparison = (w?: string) => {
|
||||
if (!w) return w;
|
||||
const k = String(w).toLowerCase().normalize('NFD').replace(/\p{Diacritic}/gu, '').replace(/[^a-z0-9]/g, '');
|
||||
const map: Record<string,string> = {
|
||||
'segunda':'segunda','terca':'terca','quarta':'quarta','quinta':'quinta','sexta':'sexta','sabado':'sabado','domingo':'domingo',
|
||||
'monday':'segunda','tuesday':'terca','wednesday':'quarta','thursday':'quinta','friday':'sexta','saturday':'sabado','sunday':'domingo',
|
||||
'1':'segunda','2':'terca','3':'quarta','4':'quinta','5':'sexta','6':'sabado','0':'domingo','7':'domingo'
|
||||
};
|
||||
return map[k] ?? k;
|
||||
};
|
||||
|
||||
// Get list of already used weekdays (excluding current one in edit mode)
|
||||
const usedWeekdays = new Set(
|
||||
(existingAvailabilities || [])
|
||||
.filter(a => mode === 'edit' ? a.id !== availability?.id : true)
|
||||
.map(a => normalizeWeekdayForComparison(a.weekday))
|
||||
.filter(Boolean)
|
||||
);
|
||||
|
||||
// When editing, populate state from availability prop
|
||||
useEffect(() => {
|
||||
if (mode === 'edit' && availability) {
|
||||
@ -47,6 +69,17 @@ export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved,
|
||||
}
|
||||
}, [mode, availability])
|
||||
|
||||
// When creating and modal opens, set the first available weekday
|
||||
useEffect(() => {
|
||||
if (mode === 'create' && open) {
|
||||
const allWeekdays = ['segunda', 'terca', 'quarta', 'quinta', 'sexta', 'sabado', 'domingo'];
|
||||
const firstAvailable = allWeekdays.find(day => !usedWeekdays.has(day));
|
||||
if (firstAvailable) {
|
||||
setWeekday(firstAvailable);
|
||||
}
|
||||
}
|
||||
}, [mode, open, usedWeekdays])
|
||||
|
||||
async function handleSubmit(e?: React.FormEvent) {
|
||||
e?.preventDefault()
|
||||
if (!doctorId) {
|
||||
@ -181,25 +214,25 @@ export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved,
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Criar disponibilidade</DialogTitle>
|
||||
<DialogTitle>{mode === 'edit' ? 'Editar disponibilidade' : 'Criar disponibilidade'}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4 py-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>Dia da semana</Label>
|
||||
<Select value={weekday} onValueChange={(v) => setWeekday(v)}>
|
||||
<Select value={weekday} onValueChange={(v) => setWeekday(v)} disabled={mode === 'edit'}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="segunda">Segunda</SelectItem>
|
||||
<SelectItem value="terca">Terça</SelectItem>
|
||||
<SelectItem value="quarta">Quarta</SelectItem>
|
||||
<SelectItem value="quinta">Quinta</SelectItem>
|
||||
<SelectItem value="sexta">Sexta</SelectItem>
|
||||
<SelectItem value="sabado">Sábado</SelectItem>
|
||||
<SelectItem value="domingo">Domingo</SelectItem>
|
||||
<SelectItem value="segunda" disabled={usedWeekdays.has('segunda')}>Segunda</SelectItem>
|
||||
<SelectItem value="terca" disabled={usedWeekdays.has('terca')}>Terça</SelectItem>
|
||||
<SelectItem value="quarta" disabled={usedWeekdays.has('quarta')}>Quarta</SelectItem>
|
||||
<SelectItem value="quinta" disabled={usedWeekdays.has('quinta')}>Quinta</SelectItem>
|
||||
<SelectItem value="sexta" disabled={usedWeekdays.has('sexta')}>Sexta</SelectItem>
|
||||
<SelectItem value="sabado" disabled={usedWeekdays.has('sabado')}>Sábado</SelectItem>
|
||||
<SelectItem value="domingo" disabled={usedWeekdays.has('domingo')}>Domingo</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@ -242,7 +275,7 @@ export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved,
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="ghost" onClick={() => onOpenChange(false)} disabled={submitting}>Cancelar</Button>
|
||||
<Button type="submit" disabled={submitting}>{submitting ? 'Salvando...' : 'Criar disponibilidade'}</Button>
|
||||
<Button type="submit" disabled={submitting}>{submitting ? 'Salvando...' : (mode === 'edit' ? 'Salvar alterações' : 'Criar disponibilidade')}</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user