Skip to content

Commit ca09ebf

Browse files
committed
feat: suggested style/behavior changes for form switcher
1 parent f24140d commit ca09ebf

File tree

6 files changed

+102
-64
lines changed

6 files changed

+102
-64
lines changed

core/app/c/[communitySlug]/pubs/[pubId]/edit/page.tsx

+27-26
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Label } from "ui/label";
1010

1111
import { ContentLayout } from "~/app/c/[communitySlug]/ContentLayout";
1212
import { FormSwitcher } from "~/app/components/FormSwitcher/FormSwitcher";
13-
import { PageTitleWithStatus } from "~/app/components/pubs/PubEditor/PageTitleWithStatus";
13+
import { PubPageTitleWithStatus } from "~/app/components/pubs/PubEditor/PageTitleWithStatus";
1414
import { PubEditor } from "~/app/components/pubs/PubEditor/PubEditor";
1515
import { getPageLoginData } from "~/lib/authentication/loginData";
1616
import { getAuthorizedUpdateForms, userCanEditPub } from "~/lib/authorization/capabilities";
@@ -79,35 +79,36 @@ export default async function Page(props: {
7979
const params = await props.params;
8080
const { pubId, communitySlug } = params;
8181

82-
const { user } = await getPageLoginData();
83-
8482
if (!pubId || !communitySlug) {
85-
return null;
86-
}
87-
88-
const canUpdatePub = await userCanEditPub({ userId: user.id, pubId });
89-
90-
if (!canUpdatePub) {
91-
redirect(`/c/${communitySlug}/unauthorized`);
83+
return notFound();
9284
}
9385

94-
const community = await findCommunityBySlug(communitySlug);
86+
const [{ user }, community] = await Promise.all([
87+
getPageLoginData(),
88+
findCommunityBySlug(communitySlug),
89+
]);
9590

9691
if (!community) {
9792
notFound();
9893
}
9994

100-
const pub = await getPubsWithRelatedValuesCached({
101-
pubId: params.pubId as PubsId,
102-
communityId: community.id,
103-
userId: user.id,
104-
});
95+
const [canUpdatePub, pub, availableForms] = await Promise.all([
96+
userCanEditPub({ userId: user.id, pubId }),
97+
getPubsWithRelatedValuesCached({
98+
pubId: params.pubId as PubsId,
99+
communityId: community.id,
100+
userId: user.id,
101+
}),
102+
getAuthorizedUpdateForms(user.id, params.pubId).execute(),
103+
]);
105104

106-
if (!pub) {
107-
return null;
105+
if (!canUpdatePub) {
106+
redirect(`/c/${communitySlug}/unauthorized`);
108107
}
109108

110-
const availableForms = await getAuthorizedUpdateForms(user.id, pub.id).execute();
109+
if (!pub) {
110+
return notFound();
111+
}
111112

112113
const htmlFormId = `edit-pub-${pub.id}`;
113114

@@ -118,7 +119,13 @@ export default async function Page(props: {
118119
Save
119120
</Button>
120121
}
121-
title={<PageTitleWithStatus title="Edit pub" />}
122+
title={
123+
<PubPageTitleWithStatus
124+
title="Edit pub"
125+
defaultFormSlug={searchParams.form}
126+
forms={availableForms}
127+
/>
128+
}
122129
right={
123130
<Button variant="link" asChild>
124131
<Link href={`/c/${communitySlug}/pubs/${pub.id}`}>View Pub</Link>
@@ -127,12 +134,6 @@ export default async function Page(props: {
127134
>
128135
<div className="flex justify-center py-10">
129136
<div className="max-w-prose flex-1">
130-
<Label htmlFor="edit-page-form-switcher">Current form</Label>
131-
<FormSwitcher
132-
htmlId="edit-page-form-switcher"
133-
defaultFormSlug={searchParams.form}
134-
forms={availableForms}
135-
/>
136137
{/** TODO: Add suspense */}
137138
<PubEditor
138139
searchParams={searchParams}

core/app/c/[communitySlug]/pubs/[pubId]/page.tsx

+10-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
22

33
import Link from "next/link";
44
import { notFound, redirect } from "next/navigation";
5+
import { Eye } from "lucide-react";
56

67
import type { PubsId } from "db/public";
78
import { Capabilities, MembershipType } from "db/public";
@@ -173,11 +174,18 @@ export default async function Page(props: {
173174
<div className="flex flex-col space-y-4">
174175
<div className="mb-8 flex items-center justify-between">
175176
<div>
176-
<div className="flex items-center gap-2">
177+
<div className="flex items-baseline gap-2">
177178
<span className="text-lg font-semibold text-muted-foreground">
178179
{pub.pubType.name}
179180
</span>
180-
<FormSwitcher defaultFormSlug={formSlug} forms={availableForms} />
181+
<div className="ml-4 flex items-center gap-0 text-muted-foreground">
182+
<Eye size={14} />
183+
<FormSwitcher
184+
defaultFormSlug={formSlug}
185+
forms={availableForms}
186+
className="p-1 text-xs"
187+
/>
188+
</div>
181189
</div>
182190
<h1 className="mb-2 text-xl font-bold">{getPubTitle(pub)} </h1>
183191
</div>

core/app/c/[communitySlug]/pubs/create/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Label } from "ui/label";
88

99
import { ContentLayout } from "~/app/c/[communitySlug]/ContentLayout";
1010
import { FormSwitcher } from "~/app/components/FormSwitcher/FormSwitcher";
11-
import { PageTitleWithStatus } from "~/app/components/pubs/PubEditor/PageTitleWithStatus";
11+
import { PubPageTitleWithStatus } from "~/app/components/pubs/PubEditor/PageTitleWithStatus";
1212
import { PubEditor } from "~/app/components/pubs/PubEditor/PubEditor";
1313
import { getPageLoginData } from "~/lib/authentication/loginData";
1414
import { getAuthorizedCreateForms, userCanCreatePub } from "~/lib/authorization/capabilities";
@@ -72,7 +72,7 @@ export default async function Page(props: {
7272
Save
7373
</Button>
7474
}
75-
title={<PageTitleWithStatus title="Create pub" />}
75+
title={<PubPageTitleWithStatus title="Create pub" />}
7676
right={<div />}
7777
>
7878
<div className="flex justify-center py-10">
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
"use client";
22

3-
import { useState } from "react";
4-
import { usePathname, useRouter, useSearchParams } from "next/navigation";
3+
import { useQueryState } from "nuqs";
54

6-
import { Button } from "ui/button";
7-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "ui/select";
5+
import {
6+
Select,
7+
SelectContent,
8+
SelectGroup,
9+
SelectItem,
10+
SelectLabel,
11+
SelectTrigger,
12+
} from "ui/select";
13+
import { cn } from "utils";
814

915
import type { SimpleForm } from "~/lib/server/form";
1016

@@ -14,51 +20,50 @@ export const FormSwitcher = ({
1420
forms,
1521
defaultFormSlug,
1622
htmlId,
23+
className,
1724
}: {
1825
forms: SimpleForm[];
1926
defaultFormSlug?: string;
2027
htmlId?: string;
28+
className?: string;
2129
}) => {
22-
const router = useRouter();
23-
const pathname = usePathname();
24-
const params = useSearchParams();
30+
const defaultForm = forms.find((form) => form.isDefault);
2531

26-
const [selectedFormSlug, setSelectedFormSlug] = useState(defaultFormSlug ?? forms[0].slug);
27-
if (!forms.length) {
32+
const [currentFormSlug, setCurrentFormSlug] = useQueryState(formSwitcherUrlParam, {
33+
shallow: false,
34+
defaultValue: defaultForm?.slug ?? forms[0].slug,
35+
});
36+
37+
const currentForm = forms.find((form) => form.slug === currentFormSlug);
38+
39+
if (!forms.length || forms.length === 1 || !currentForm) {
2840
return null;
2941
}
3042

31-
const switchForms = () => {
32-
if (!selectedFormSlug) {
33-
return;
34-
}
35-
const newParams = new URLSearchParams(params);
36-
newParams.set(formSwitcherUrlParam, selectedFormSlug);
37-
router.push(`${pathname}?${newParams.toString()}`);
38-
};
39-
4043
return (
4144
<div className="flex items-center gap-2">
4245
<Select
4346
onValueChange={(slug: string) => {
44-
setSelectedFormSlug(slug);
47+
setCurrentFormSlug(slug);
4548
}}
46-
defaultValue={selectedFormSlug}
49+
defaultValue={currentFormSlug || defaultFormSlug}
4750
>
48-
<SelectTrigger id={htmlId}>
49-
<SelectValue />
51+
<SelectTrigger id={htmlId} className={cn("border-none bg-transparent", className)}>
52+
{currentForm?.name}
5053
</SelectTrigger>
5154
<SelectContent>
52-
{forms.map((form) => (
53-
<SelectItem key={form.id} value={form.slug}>
54-
{form.name}
55-
</SelectItem>
56-
))}
55+
<SelectGroup>
56+
<SelectLabel className="text-xs font-normal text-muted-foreground">
57+
Content will change upon selection. You may lose unsaved changes.
58+
</SelectLabel>
59+
{forms.map((form) => (
60+
<SelectItem key={form.id} value={form.slug}>
61+
{form.name}
62+
</SelectItem>
63+
))}
64+
</SelectGroup>
5765
</SelectContent>
5866
</Select>
59-
<Button disabled={forms.length < 2} onClick={switchForms}>
60-
Switch forms
61-
</Button>
6267
</div>
6368
);
6469
};
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,39 @@
11
"use client";
22

3+
import type { SimpleForm } from "~/lib/server/form";
34
import { useSaveStatus } from "~/app/components/pubs/PubEditor/SaveStatus";
5+
import { FormSwitcher } from "../../FormSwitcher/FormSwitcher";
46

5-
export const PageTitleWithStatus = ({ title }: { title: string }) => {
7+
export const PubPageTitleWithStatus = ({
8+
title,
9+
forms,
10+
defaultFormSlug,
11+
}: {
12+
title: string | React.ReactNode;
13+
forms: SimpleForm[];
14+
defaultFormSlug?: string;
15+
}) => {
616
const status = useSaveStatus({ defaultMessage: "Form will save when you click save" });
17+
const hasMultipleForms = forms?.length > 1;
718
return (
819
<div className="flex flex-col items-center">
920
{title}
10-
<span className="text-sm font-normal text-muted-foreground" data-testid="save-status">
21+
<div
22+
className="flex items-center gap-2 text-sm font-normal text-muted-foreground"
23+
data-testid="save-status"
24+
>
1125
{status}
12-
</span>
26+
{hasMultipleForms && (
27+
<>
28+
<div className="mx-1 h-4 border-r border-gray-300"></div>
29+
<FormSwitcher
30+
forms={forms}
31+
defaultFormSlug={defaultFormSlug}
32+
className="m-0 h-6 bg-none p-0 text-sm shadow-none focus-visible:ring-2 focus-visible:ring-offset-1"
33+
/>
34+
</>
35+
)}
36+
</div>
1337
</div>
1438
);
1539
};

packages/ui/src/select.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
1919
<SelectPrimitive.Trigger
2020
ref={ref}
2121
className={cn(
22-
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:bg-gray-200 disabled:opacity-50 [&>span]:line-clamp-1",
22+
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:bg-gray-200 disabled:opacity-50 [&>span]:line-clamp-1",
2323
className
2424
)}
2525
{...props}

0 commit comments

Comments
 (0)