Skip to content

Commit 0ca97ab

Browse files
committed
feat(ui): display organization name on home and profile views
1 parent 7beec40 commit 0ca97ab

8 files changed

Lines changed: 64 additions & 7 deletions

File tree

src/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ export default function App() {
248248
view={state.view}
249249
onNavigate={navigate}
250250
userEmail={session?.user.email}
251+
organizationName={
252+
state.project?.organization || state.projects?.[0]?.organization
253+
}
251254
isPreview={!configured}
252255
observations={state.observations}
253256
lastSyncAt={state.lastSyncAt}

src/app/useAppController.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
defaultOrganizationName,
4646
loadAssignedProjects,
4747
loadUserAdminAccess,
48+
loadUserOrganizationName,
4849
updateProjectStatus,
4950
} from "../lib/adminBackend";
5051
import { downloadUrl } from "../lib/download";
@@ -294,7 +295,7 @@ export function useAppController() {
294295
}
295296
if (!active) return;
296297
setAdminAccess(hasAdminAccess ? "allowed" : "denied");
297-
if (remoteProjects.length)
298+
if (remoteProjects.length) {
298299
setState((current) => ({
299300
...current,
300301
projects: remoteProjects,
@@ -303,17 +304,27 @@ export function useAppController() {
303304
(candidate) => candidate.id === current.project.id,
304305
) ?? remoteProjects[0],
305306
}));
306-
else
307+
} else {
308+
const userOrg = hasAdminAccess
309+
? await loadUserOrganizationName().catch(() => null)
310+
: null;
307311
setState((current) => ({
308312
...current,
309313
projects: [],
310314
// Confirmed empty assignment: hide the cached project so a
311315
// revoked contributor cannot keep collecting offline into
312-
// ACTION_REQUIRED records.
313-
project: emptyProject,
316+
// ACTION_REQUIRED records. Keep the organization name for clarity.
317+
project: {
318+
...emptyProject,
319+
organization:
320+
userOrg ??
321+
current.project.organization ??
322+
defaultOrganizationName,
323+
},
314324
mode: current.mode,
315325
view: current.mode === "admin" ? "admin" : "home",
316326
}));
327+
}
317328
};
318329
// Invites are claimed before the project list loads: an already-registered
319330
// contributor receives membership only through claim-invites, and the list

src/components/AdminDashboard.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,15 @@ export function AdminDashboard({
9696
setAddingAdministrator(false);
9797
}
9898
};
99+
const organizationName = project.organization || projects[0]?.organization;
100+
99101
return (
100102
<main className="page page-admin">
101103
<div className="page-heading admin-heading">
102-
<h1>Projects</h1>
104+
<div className="page-heading-copy">
105+
{organizationName && <Eyebrow>{organizationName}</Eyebrow>}
106+
<h1>Projects</h1>
107+
</div>
103108
<div className="primary-action-dock">
104109
<Button
105110
variant="primary"

src/components/ContributorHome.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,12 @@ export function ContributorHome({
7979
).length;
8080
const syncedCount = project?.completeSubmissions ?? 0;
8181

82+
const organizationName = project?.organization;
83+
8284
return (
8385
<main className="page page-contributor">
8486
<div className="page-heading page-heading-home">
87+
{organizationName && <Eyebrow>{organizationName}</Eyebrow>}
8588
<h1>Fieldwork</h1>
8689
</div>
8790

src/components/ProfileSheet.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import { APP_VERSION, FEEDBACK_URL } from "../lib/appMeta";
1414
interface ProfileSheetProps {
1515
userEmail?: string | null;
1616
profile: ContributorProfile | null;
17-
observations: Observation[];
17+
organizationName?: string | null;
18+
observations?: Observation[];
1819
lastSyncAt: string | null;
1920
isAdmin: boolean;
2021
isPreview: boolean;
@@ -27,6 +28,7 @@ interface ProfileSheetProps {
2728
export function ProfileSheet({
2829
userEmail,
2930
profile,
31+
organizationName,
3032
observations = [],
3133
lastSyncAt,
3234
isAdmin,
@@ -36,6 +38,7 @@ export function ProfileSheet({
3638
onRecoveryExport,
3739
onSignOut,
3840
}: ProfileSheetProps) {
41+
const profileOrganization = organizationName?.trim();
3942
const pendingCount = (observations ?? []).filter(
4043
(observation) => observation.status !== "SYNCED",
4144
).length;
@@ -64,6 +67,9 @@ export function ProfileSheet({
6467
? "Preview session"
6568
: (userEmail ?? "Account email unavailable")}
6669
</p>
70+
{profileOrganization && (
71+
<p className="profile-organization">{profileOrganization}</p>
72+
)}
6773

6874
{!isAdmin && (
6975
<>

src/components/TopBar.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface TopBarProps {
1010
view: View;
1111
onNavigate: (view: View) => void;
1212
userEmail?: string | null;
13+
organizationName?: string | null;
1314
isPreview?: boolean;
1415
observations?: Observation[];
1516
lastSyncAt?: string | null;
@@ -25,6 +26,7 @@ export function TopBar({
2526
view,
2627
onNavigate,
2728
userEmail,
29+
organizationName,
2830
isPreview = false,
2931
observations = [],
3032
lastSyncAt = null,
@@ -83,6 +85,7 @@ export function TopBar({
8385
<ProfileSheet
8486
userEmail={userEmail}
8587
profile={profile}
88+
organizationName={organizationName}
8689
observations={observations}
8790
lastSyncAt={lastSyncAt}
8891
isAdmin={isAdmin}

src/lib/adminBackend.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,24 @@ export async function loadUserAdminAccess(): Promise<boolean> {
156156
return Boolean(orgAdmin) || Boolean(projectAdmin);
157157
}
158158

159+
export async function loadUserOrganizationName(): Promise<string | null> {
160+
const client = requireClient();
161+
const { data: userData, error: userError } = await client.auth.getUser();
162+
if (userError || !userData.user) return null;
163+
const { data: membership } = await client
164+
.from("organization_members")
165+
.select("organization_id, organizations(name)")
166+
.eq("user_id", userData.user.id)
167+
.eq("role", "admin")
168+
.limit(1)
169+
.maybeSingle();
170+
if (membership?.organizations) {
171+
const org = membership.organizations as unknown as { name?: string };
172+
if (org.name) return org.name;
173+
}
174+
return null;
175+
}
176+
159177
export async function createRemoteProject(
160178
input: NewProjectInput,
161179
): Promise<Project> {

src/styles/geometry.css

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2003,14 +2003,22 @@ html[data-keyboard-open="true"] .page-wizard .wizard-actions .button-secondary {
20032003
margin-bottom: var(--space-2);
20042004
}
20052005
.profile-email {
2006-
margin: 0 0 var(--space-5);
2006+
margin: 0 0 var(--space-1);
20072007
overflow: hidden;
20082008
color: var(--secondary);
20092009
font-size: var(--type-footnote);
20102010
line-height: 1.35;
20112011
text-overflow: ellipsis;
20122012
white-space: nowrap;
20132013
}
2014+
.profile-organization {
2015+
margin: 0 0 var(--space-5);
2016+
color: var(--tertiary);
2017+
font-size: var(--type-caption);
2018+
font-weight: 500;
2019+
text-transform: uppercase;
2020+
letter-spacing: 0.05em;
2021+
}
20142022
.profile-metrics {
20152023
display: grid;
20162024
grid-template-columns: repeat(2, minmax(0, 1fr));

0 commit comments

Comments
 (0)