+ setExpandedCommentsCaseId((prev) =>
+ prev === caseGroup.labCaseId ? null : caseGroup.labCaseId,
+ )
+ }
+ />
+ {expandedCommentsCaseId === caseGroup.labCaseId && canEdit ? (
+
+ {
+ const r = await tasksApi.listComments(caseGroup.labCaseId);
+ return r.data;
+ }}
+ onPost={async (body, visibleToClinic) => {
+ const r = await tasksApi.addComment(caseGroup.labCaseId, {
+ body,
+ visibleToClinic,
+ });
+ notifyTabBadgesChanged();
+ return r.data;
+ }}
+ onToggleVisibility={async (commentId, visible) => {
+ const r = await tasksApi.setCommentVisibility(commentId, visible);
+ return r.data;
+ }}
+ onError={showError}
+ />
+
+ ) : null}
{caseGroup.prosthesisGroups.map((prosthesisGroup) => (
=> {
+ const response = await apiClient.get(`/lab-cases/access/${encodeURIComponent(token)}`);
+ return response.data;
+ },
+
+ listTasks: async (
+ token: string,
+ ): Promise<{ success: boolean; data: { items: LabTaskListItem[] } }> => {
+ const response = await apiClient.get(
+ `/lab-cases/access/${encodeURIComponent(token)}/tasks`,
+ );
+ return response.data;
+ },
+
+ listComments: async (
+ token: string,
+ ): Promise<{ success: boolean; data: LabCaseComment[] }> => {
+ const response = await apiClient.get(
+ `/lab-cases/access/${encodeURIComponent(token)}/comments`,
+ );
+ return response.data;
+ },
+
+ addComment: async (
+ token: string,
+ payload: { body: string; visibleToClinic?: boolean },
+ ): Promise<{ success: boolean; data: LabCaseComment }> => {
+ const response = await apiClient.post(
+ `/lab-cases/access/${encodeURIComponent(token)}/comments`,
+ payload,
+ );
+ return response.data;
+ },
+
+ setCommentVisibility: async (
+ token: string,
+ commentId: string,
+ visibleToClinic: boolean,
+ ): Promise<{ success: boolean; data: LabCaseComment }> => {
+ const response = await apiClient.patch(
+ `/lab-cases/access/${encodeURIComponent(token)}/comments/${commentId}/visibility`,
+ { visibleToClinic },
+ );
+ return response.data;
+ },
+};
diff --git a/frontend/src/lib/auth/postAuthRedirect.ts b/frontend/src/lib/auth/postAuthRedirect.ts
new file mode 100644
index 0000000..a0155a3
--- /dev/null
+++ b/frontend/src/lib/auth/postAuthRedirect.ts
@@ -0,0 +1,17 @@
+import { stripLocaleFromPathname } from '@/i18n/routing';
+
+const AUTH_REDIRECT_KEY = 'authRedirect';
+
+export function storeAuthRedirectFromPath(pathWithOptionalLocale: string) {
+ if (typeof window === 'undefined') return;
+ const path = stripLocaleFromPathname(pathWithOptionalLocale);
+ if (!path || path === '/login' || path === '/register') return;
+ sessionStorage.setItem(AUTH_REDIRECT_KEY, path);
+}
+
+export function consumeAuthRedirect(): string | null {
+ if (typeof window === 'undefined') return null;
+ const path = sessionStorage.getItem(AUTH_REDIRECT_KEY);
+ if (path) sessionStorage.removeItem(AUTH_REDIRECT_KEY);
+ return path;
+}
diff --git a/frontend/src/lib/hooks/useAuth.tsx b/frontend/src/lib/hooks/useAuth.tsx
index bf7d66a..d534d5a 100644
--- a/frontend/src/lib/hooks/useAuth.tsx
+++ b/frontend/src/lib/hooks/useAuth.tsx
@@ -17,6 +17,7 @@ import {
startProactiveSessionRefresh,
} from '@/lib/auth/proactiveRefresh';
import { asApiError, legacyStatusCode, type ApiError } from '@/types/api';
+import { consumeAuthRedirect } from '@/lib/auth/postAuthRedirect';
function toApiError(err: unknown): ApiError {
return (
@@ -288,10 +289,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
await authApi.selectOrganization(org.id);
setCurrentOrganization(org);
localStorage.setItem('currentOrganizationId', org.id);
- router.push('/today');
} else {
router.push('/select-organization');
-
}
} catch (err: any) {
@@ -332,7 +331,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
await authApi.selectOrganization(org.id);
setCurrentOrganization(org);
localStorage.setItem('currentOrganizationId', org.id);
- router.push('/today');
} else {
router.push('/select-organization');
}
@@ -388,12 +386,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
plan: (organization as { plan?: Organization['plan'] }).plan,
});
- const redirectPath =
- typeof window !== 'undefined'
- ? sessionStorage.getItem('authRedirect')
- : null;
+ const redirectPath = consumeAuthRedirect();
if (redirectPath) {
- sessionStorage.removeItem('authRedirect');
router.push(redirectPath);
} else {
router.push('/today');
diff --git a/frontend/src/types/cases.ts b/frontend/src/types/cases.ts
index 7effe96..abde6ce 100644
--- a/frontend/src/types/cases.ts
+++ b/frontend/src/types/cases.ts
@@ -119,6 +119,7 @@ export interface LabCaseDetail {
tasks: LabCaseTask[];
tasksByTooth: LabCaseTaskGroup[];
taskProgress: { completed: number; total: number };
+ shareUrl?: string | null;
}
export interface ListLabCasesParams {