Merge branch 'master' into feature/mobile-responsive
This commit is contained in:
@@ -189,6 +189,9 @@ export default function AppointmentsPage() {
|
||||
}
|
||||
|
||||
function handleSlotClick(startMinute: number, providerUserId: string, providerName: string) {
|
||||
if (!canManageAppointments) {
|
||||
return;
|
||||
}
|
||||
if (isViewingPastDay) {
|
||||
toast.showInfo(t('infoPastViewOnly'));
|
||||
return;
|
||||
@@ -205,6 +208,9 @@ export default function AppointmentsPage() {
|
||||
}
|
||||
|
||||
function handleAppointmentClick(appointment: AppointmentRecord) {
|
||||
if (!canManageAppointments) {
|
||||
return;
|
||||
}
|
||||
if (isViewingPastDay) {
|
||||
toast.showInfo(t('infoPastViewOnly'));
|
||||
return;
|
||||
|
||||
@@ -1,24 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Link } from '@/i18n/navigation';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import { Card } from '@/components/ui/shared/Card';
|
||||
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
|
||||
import { TodayDashboard } from '@/components/today/TodayDashboard';
|
||||
import { TodayLoadErrorBanner } from '@/components/today/TodayLoadErrorBanner';
|
||||
import { TodaySectionErrorFallback } from '@/components/today/TodaySectionErrorFallback';
|
||||
import { TodayWidgetErrorBoundary } from '@/components/today/TodayWidgetErrorBoundary';
|
||||
import { useTodaySummary } from '@/lib/hooks/useTodaySummary';
|
||||
|
||||
export default function TodayPage() {
|
||||
const t = useTranslations('today');
|
||||
const { currentOrganization } = useAuth();
|
||||
const showNoSubscriptionNotice =
|
||||
Boolean(currentOrganization?.isOwner) && !currentOrganization?.plan;
|
||||
const orgId = currentOrganization?.id;
|
||||
const { data, loading, isInitialLoad, error, reload } = useTodaySummary(orgId);
|
||||
|
||||
const showNoSubscriptionNotice = useMemo(
|
||||
() => Boolean(currentOrganization?.isOwner) && !currentOrganization?.plan,
|
||||
[currentOrganization],
|
||||
);
|
||||
|
||||
const sectionErrorMessage = t('sectionLoadError');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-xl sm:text-2xl font-semibold mb-4 sm:mb-6">
|
||||
{t('welcomeBack')}
|
||||
</h1>
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col gap-1 sm:flex-row sm:items-end sm:justify-between">
|
||||
<h1 className="text-2xl font-semibold">{t('welcomeBack')}</h1>
|
||||
{data?.generatedAt && !isInitialLoad ? (
|
||||
<p className="text-xs text-text-muted">
|
||||
{t('lastUpdated', {
|
||||
time: new Intl.DateTimeFormat(undefined, {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
}).format(new Date(data.generatedAt)),
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{showNoSubscriptionNotice && (
|
||||
<div className="mb-6 rounded-[var(--radius-md)] border border-amber-500/30 bg-amber-500/10 p-4">
|
||||
<div className="rounded-[var(--radius-md)] border border-amber-500/30 bg-amber-500/10 p-4">
|
||||
<p className="text-sm text-amber-200">
|
||||
{t('noSubscriptionNotice')}{' '}
|
||||
<Link href="/settings/subscriptions" className="font-medium underline underline-offset-2">
|
||||
@@ -29,27 +52,28 @@ export default function TodayPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
|
||||
<Card>
|
||||
<p className="text-sm text-card-muted">{t('cardTodaysAppointments')}</p>
|
||||
<p className="text-2xl font-semibold mt-2">12</p>
|
||||
<p className="text-xs text-text-muted mt-1">Monday 2/5/2026</p>
|
||||
</Card>
|
||||
<Card>
|
||||
<p className="text-sm text-card-muted">{t('cardActivePatients')}</p>
|
||||
<p className="text-2xl font-semibold mt-2">675</p>
|
||||
</Card>
|
||||
<Card>
|
||||
<p className="text-sm text-card-muted">{t('cardNewLabCase')}</p>
|
||||
<p className="text-2xl font-semibold mt-2">5</p>
|
||||
<p className="text-xs text-text-muted mt-1">35 ↑</p>
|
||||
</Card>
|
||||
<Card>
|
||||
<p className="text-sm text-card-muted">{t('cardTodayInvoices')}</p>
|
||||
<p className="text-2xl font-semibold mt-2">1200$</p>
|
||||
<p className="text-xs text-text-muted mt-1">21,300 $</p>
|
||||
</Card>
|
||||
</div>
|
||||
{error ? (
|
||||
<TodayLoadErrorBanner
|
||||
message={formatApiErrorMessage(error, t('loadError'))}
|
||||
retryLabel={t('retryLoad')}
|
||||
onRetry={() => void reload()}
|
||||
isRetrying={loading && Boolean(data)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<TodayWidgetErrorBoundary
|
||||
fallback={<TodaySectionErrorFallback message={sectionErrorMessage} />}
|
||||
>
|
||||
<TodayDashboard
|
||||
widgets={data?.widgets ?? {}}
|
||||
charts={data?.charts ?? {}}
|
||||
actions={data?.actions ?? {}}
|
||||
subscription={data?.subscription}
|
||||
loading={loading}
|
||||
isInitialLoad={isInitialLoad}
|
||||
hasError={Boolean(error)}
|
||||
/>
|
||||
</TodayWidgetErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { TreatmentWorkspace } from '@/components/ui/treatment/TreatmentWorkspace';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
|
||||
export default function TreatmentPage() {
|
||||
const t = useTranslations('treatment');
|
||||
const { user, currentOrganization, isAuthReady } = useAuth();
|
||||
const searchParams = useSearchParams();
|
||||
const initialAppointmentId = searchParams.get('appointmentId');
|
||||
|
||||
if (!isAuthReady || !user) {
|
||||
return (
|
||||
@@ -15,6 +18,10 @@ export default function TreatmentPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<TreatmentWorkspace userId={user.id} currentOrganization={currentOrganization} />
|
||||
<TreatmentWorkspace
|
||||
userId={user.id}
|
||||
currentOrganization={currentOrganization}
|
||||
initialAppointmentId={initialAppointmentId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user