Skip to content

Commit 09bbcd2

Browse files
committed
Various improvements and refactors
1 parent e0a8b59 commit 09bbcd2

File tree

1 file changed

+69
-90
lines changed
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index

1 file changed

+69
-90
lines changed

apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index/route.tsx

Lines changed: 69 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,16 @@ import { Button } from "~/components/primitives/Buttons";
3434
import { Fieldset } from "~/components/primitives/Fieldset";
3535
import { FormButtons } from "~/components/primitives/FormButtons";
3636
import { FormError } from "~/components/primitives/FormError";
37-
import { Header2, Header3 } from "~/components/primitives/Headers";
37+
import { Header2 } from "~/components/primitives/Headers";
3838
import { Hint } from "~/components/primitives/Hint";
3939
import { Input } from "~/components/primitives/Input";
4040
import { InputGroup } from "~/components/primitives/InputGroup";
4141
import { Label } from "~/components/primitives/Label";
4242
import { NavBar, PageTitle } from "~/components/primitives/PageHeader";
43-
import {
44-
Popover,
45-
PopoverContent,
46-
PopoverCustomTrigger,
47-
PopoverTrigger,
48-
} from "~/components/primitives/Popover";
49-
import { Spinner, SpinnerWhite } from "~/components/primitives/Spinner";
43+
import { Popover, PopoverContent, PopoverTrigger } from "~/components/primitives/Popover";
44+
import { SpinnerWhite } from "~/components/primitives/Spinner";
5045
import { prisma } from "~/db.server";
5146
import { useFaviconUrl } from "~/hooks/useFaviconUrl";
52-
import { useOrganization } from "~/hooks/useOrganizations";
5347
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
5448
import { clearCurrentProject } from "~/services/dashboardPreferences.server";
5549
import { DeleteOrganizationService } from "~/services/deleteOrganization.server";
@@ -59,7 +53,6 @@ import { cn } from "~/utils/cn";
5953
import { extractDomain, faviconUrl as buildFaviconUrl } from "~/utils/favicon";
6054
import {
6155
OrganizationParamsSchema,
62-
organizationPath,
6356
organizationSettingsPath,
6457
rootPath,
6558
} from "~/utils/pathBuilder";
@@ -92,14 +85,11 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
9285
throw new Response("Not found", { status: 404 });
9386
}
9487

95-
const onboardingData =
96-
organization.onboardingData && typeof organization.onboardingData === "object"
97-
? (organization.onboardingData as Record<string, unknown>)
98-
: {};
88+
const onboardingData = toRecord(organization.onboardingData);
9989

10090
const parsedAvatar = parseAvatar(organization.avatar, defaultAvatar);
10191
const lastIconHex =
102-
parsedAvatar.type === "image" && "lastIconHex" in parsedAvatar && typeof parsedAvatar.lastIconHex === "string"
92+
parsedAvatar.type === "image" && parsedAvatar.lastIconHex
10393
? parsedAvatar.lastIconHex
10494
: defaultAvatarHex;
10595

@@ -236,19 +226,9 @@ export const action: ActionFunction = async ({ request, params }) => {
236226
select: { avatar: true, onboardingData: true },
237227
});
238228

239-
const existingData =
240-
existing?.onboardingData && typeof existing.onboardingData === "object"
241-
? (existing.onboardingData as Record<string, unknown>)
242-
: {};
243-
244-
const existingAvatarJson = existing ? existing.avatar : null;
245-
const existingAvatar = parseAvatar(existingAvatarJson, defaultAvatar);
246-
const lastIconHex =
247-
"hex" in existingAvatar
248-
? existingAvatar.hex
249-
: existingAvatar.type === "image" && "lastIconHex" in existingAvatar
250-
? (existingAvatar.lastIconHex as string)
251-
: undefined;
229+
const existingData = toRecord(existing?.onboardingData);
230+
const existingAvatar = parseAvatar(existing?.avatar ?? null, defaultAvatar);
231+
const lastIconHex = extractLastIconHex(existingAvatar);
252232

253233
await prisma.organization.update({
254234
where: orgWhere,
@@ -293,8 +273,9 @@ export const action: ActionFunction = async ({ request, params }) => {
293273
);
294274
}
295275
}
296-
} catch (error: any) {
297-
return json({ errors: { body: error.message } }, { status: 400 });
276+
} catch (error: unknown) {
277+
const message = error instanceof Error ? error.message : "An unexpected error occurred";
278+
return json({ errors: { body: message } }, { status: 400 });
298279
}
299280
};
300281

@@ -451,7 +432,7 @@ function LogoForm({
451432
const hex =
452433
"hex" in avatar
453434
? avatar.hex
454-
: avatar.type === "image" && "lastIconHex" in avatar && avatar.lastIconHex
435+
: avatar.type === "image" && avatar.lastIconHex
455436
? avatar.lastIconHex
456437
: organization.lastIconHex;
457438
const mode: "logo" | "icon" = avatar.type === "image" ? "logo" : "icon";
@@ -489,24 +470,14 @@ function LogoForm({
489470
<input type="hidden" name="action" value="avatar" />
490471
<input type="hidden" name="type" value="image" />
491472
<input type="hidden" name="url" value={companyUrl} />
492-
<button
493-
type="submit"
494-
className="flex shrink-0 items-center gap-3"
495-
>
496-
<div
497-
className={cn(
498-
"flex shrink-0 items-center justify-center rounded-full border-2 p-0.5 transition",
499-
mode === "logo" ? "border-indigo-500" : "border-charcoal-700 hover:border-charcoal-600"
500-
)}
501-
>
502-
<div className="size-2 rounded-full" style={{ backgroundColor: mode === "logo" ? "#6366f1" : "transparent" }} />
503-
</div>
473+
<button type="submit" className="flex shrink-0 items-center gap-3">
474+
<RadioDot active={mode === "logo"} />
504475
</button>
505476
<div className="flex flex-1 items-center gap-1.5">
506477
<button
507478
type="submit"
508479
className={cn(
509-
"box-content grid size-10 shrink-0 place-items-center rounded-sm border-2 bg-charcoal-775",
480+
iconTileClass,
510481
mode === "logo"
511482
? "border-indigo-500"
512483
: "border-charcoal-775 hover:border-charcoal-600"
@@ -551,14 +522,8 @@ function LogoForm({
551522
<input type="hidden" name="action" value="avatar" />
552523
<input type="hidden" name="type" value="letters" />
553524
<input type="hidden" name="hex" value={hex} />
554-
<button
555-
type="submit"
556-
className={cn(
557-
"flex items-center justify-center rounded-full border-2 p-0.5 transition",
558-
mode === "icon" ? "border-indigo-500" : "border-charcoal-700 hover:border-charcoal-600"
559-
)}
560-
>
561-
<div className="size-2 rounded-full" style={{ backgroundColor: mode === "icon" ? "#6366f1" : "transparent" }} />
525+
<button type="submit">
526+
<RadioDot active={mode === "icon"} />
562527
</button>
563528
</Form>
564529
<div className="flex flex-wrap items-center gap-1.5">
@@ -570,10 +535,8 @@ function LogoForm({
570535
<button
571536
type="submit"
572537
className={cn(
573-
"box-content grid size-10 place-items-center rounded-sm border-2 bg-charcoal-775",
574-
avatar.type === "letters"
575-
? undefined
576-
: "border-charcoal-775 hover:border-charcoal-600"
538+
iconTileClass,
539+
avatar.type !== "letters" && "border-charcoal-775 hover:border-charcoal-600"
577540
)}
578541
style={{
579542
borderColor: avatar.type === "letters" ? hex : undefined,
@@ -597,10 +560,9 @@ function LogoForm({
597560
<button
598561
type="submit"
599562
className={cn(
600-
"box-content grid size-10 place-items-center rounded-sm border-2 bg-charcoal-775",
601-
avatar.type === "icon" && avatar.name === name
602-
? undefined
603-
: "border-charcoal-775 hover:border-charcoal-600"
563+
iconTileClass,
564+
!(avatar.type === "icon" && avatar.name === name) &&
565+
"border-charcoal-775 hover:border-charcoal-600"
604566
)}
605567
style={{
606568
borderColor:
@@ -629,18 +591,18 @@ function LogoForm({
629591
function HexPopover({ avatar, hex }: { avatar: Avatar; hex: string }) {
630592
return (
631593
<Popover>
632-
<PopoverTrigger className="box-content grid size-10 place-items-center rounded-sm border-2 border-charcoal-775 bg-charcoal-775 hover:border-charcoal-600">
594+
<PopoverTrigger className={cn(iconTileClass, "border-charcoal-775 hover:border-charcoal-600")}>
633595
<img src={colorWheelIcon} className="m-0 block size-[30px] p-0" />
634596
</PopoverTrigger>
635597
<PopoverContent
636598
className="overflow-y-auto p-1 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
637599
align="start"
638600
style={{ maxHeight: `calc(var(--radix-popover-content-available-height) - 10vh)` }}
639601
>
640-
<Form method="post" className="flex w-fit min-w-40 flex-col gap-1 ">
602+
<Form method="post" className="flex w-fit min-w-40 flex-col gap-1">
641603
<input type="hidden" name="action" value="avatar" />
642-
<input type="hidden" name="type" value={avatar.type} />
643-
{"name" in avatar && <input type="hidden" name="name" value={avatar.name} />}
604+
<input type="hidden" name="type" value={avatar.type === "image" ? "letters" : avatar.type} />
605+
{avatar.type === "icon" && <input type="hidden" name="name" value={avatar.name} />}
644606
{defaultAvatarColors.map((color) => (
645607
<Button
646608
key={color.hex}
@@ -674,35 +636,52 @@ function HexPopover({ avatar, hex }: { avatar: Avatar; hex: string }) {
674636
);
675637
}
676638

677-
function avatarFromFormData(formData: FormData): AvatarT | undefined {
678-
const action = formData.get("action");
679-
if (!action || action !== "avatar") {
680-
return undefined;
681-
}
639+
function RadioDot({ active }: { active: boolean }) {
640+
return (
641+
<div
642+
className={cn(
643+
"flex shrink-0 items-center justify-center rounded-full border-2 p-0.5 transition",
644+
active ? "border-indigo-500" : "border-charcoal-700 hover:border-charcoal-600"
645+
)}
646+
>
647+
<div
648+
className="size-2 rounded-full"
649+
style={{ backgroundColor: active ? "#6366f1" : "transparent" }}
650+
/>
651+
</div>
652+
);
653+
}
682654

683-
const type = formData.get("type");
684-
const hex = formData.get("hex");
655+
const iconTileClass = "box-content grid size-10 shrink-0 place-items-center rounded-sm border-2 bg-charcoal-775";
685656

686-
if (type === "letters") {
687-
return {
688-
type: "letters",
689-
hex: hex as string,
690-
};
691-
}
657+
function toRecord(json: unknown): Record<string, unknown> {
658+
return json && typeof json === "object" ? (json as Record<string, unknown>) : {};
659+
}
692660

693-
if (type === "icon") {
694-
return {
695-
type: "icon",
696-
name: formData.get("name") as string,
697-
hex: hex as string,
698-
};
699-
}
661+
function extractLastIconHex(avatar: AvatarT): string | undefined {
662+
if ("hex" in avatar) return avatar.hex;
663+
if (avatar.type === "image") return avatar.lastIconHex;
664+
return undefined;
665+
}
700666

701-
if (type === "image") {
702-
const url = formData.get("url") as string;
703-
const domain = url ? extractDomain(url) : null;
704-
return { type: "image", url: domain ? buildFaviconUrl(domain) : "" };
705-
}
667+
function avatarFromFormData(formData: FormData): AvatarT | undefined {
668+
const action = formData.get("action");
669+
if (action !== "avatar") return undefined;
706670

707-
return undefined;
671+
const type = formData.get("type");
672+
const hex = formData.get("hex") as string;
673+
674+
switch (type) {
675+
case "letters":
676+
return { type: "letters", hex };
677+
case "icon":
678+
return { type: "icon", name: formData.get("name") as string, hex };
679+
case "image": {
680+
const url = formData.get("url") as string;
681+
const domain = url ? extractDomain(url) : null;
682+
return { type: "image", url: domain ? buildFaviconUrl(domain) : "" };
683+
}
684+
default:
685+
return undefined;
686+
}
708687
}

0 commit comments

Comments
 (0)