Skip to content

Commit

Permalink
Use styled instead of css
Browse files Browse the repository at this point in the history
  • Loading branch information
horia141 committed May 14, 2023
1 parent 59ac938 commit c8aac79
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 124 deletions.
2 changes: 1 addition & 1 deletion src/webui/app/routes/workspace/chores/$id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export default function Chore() {
</FormControl>

<FormControl fullWidth>
<InputLabel id="startAtDate">Start At date</InputLabel>
<InputLabel id="startAtDate">Start At Date</InputLabel>
<OutlinedInput
type="date"
label="startAtDate"
Expand Down
221 changes: 221 additions & 0 deletions src/webui/app/routes/workspace/smart-lists/$key/items/details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import {
Button,
ButtonGroup,
Divider,
FormControl,
InputLabel,
OutlinedInput,
Stack,
} from "@mui/material";
import type { ActionArgs, LoaderArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import {
useActionData,
useCatch,
useParams,
useTransition,
} from "@remix-run/react";
import { ReasonPhrases, StatusCodes } from "http-status-codes";
import { ApiError, SmartListService } from "jupiter-gen";
import { useContext } from "react";
import { z } from "zod";
import { parseForm, parseParams } from "zodix";
import { IconSelector } from "~/components/icon-selector";
import { FieldError, GlobalError } from "~/components/infra/errors";
import { LeafCard } from "~/components/infra/leaf-card";
import { GlobalPropertiesContext } from "~/global-properties-client";
import { validationErrorToUIErrorInfo } from "~/logic/action-result";
import { isDevelopment } from "~/logic/domain/env";
import { getIntent } from "~/logic/intent";
import { useLoaderDataSafeForAnimation } from "~/rendering/use-loader-data-for-animation";
import { DisplayType } from "~/rendering/use-nested-entities";

const ParamsSchema = {
key: z.string(),
};

const UpdateFormSchema = {
intent: z.string(),
name: z.string(),
icon: z.string().optional(),
};

export const handle = {
displayType: DisplayType.LEAF,
};

export async function loader({ params }: LoaderArgs) {
const { key } = parseParams(params, ParamsSchema);

try {
const response = await SmartListService.loadSmartList({
key: { the_key: key },
allow_archived: true,
});

return json({
smartList: response.smart_list,
});
} catch (error) {
if (error instanceof ApiError && error.status === StatusCodes.NOT_FOUND) {
throw new Response(ReasonPhrases.NOT_FOUND, {
status: StatusCodes.NOT_FOUND,
statusText: ReasonPhrases.NOT_FOUND,
});
}

throw error;
}
}

export async function action({ request, params }: ActionArgs) {
const { key } = parseParams(params, ParamsSchema);
const form = await parseForm(request, UpdateFormSchema);

const { intent } = getIntent<undefined>(form.intent);

try {
switch (intent) {
case "update": {
await SmartListService.updateSmartList({
key: { the_key: key },
name: {
should_change: true,
value: { the_name: form.name },
},
icon: {
should_change: true,
value: form.icon ? { the_icon: form.icon } : undefined,
},
});

return redirect(`/workspace/smart-lists/${key}/items/details`);
}

case "archive": {
await SmartListService.archiveSmartList({
key: { the_key: key },
});
return redirect("/workspace/smart-lists");
}

default:
return new Response("Bad Intent", { status: 500 });
}
} catch (error) {
if (
error instanceof ApiError &&
error.status === StatusCodes.UNPROCESSABLE_ENTITY
) {
return json(validationErrorToUIErrorInfo(error.body));
}

throw error;
}
}

export default function SmartListDetails() {
const { key } = useParams();
const loaderData = useLoaderDataSafeForAnimation<typeof loader>();
const actionData = useActionData<typeof action>();
const transition = useTransition();

const inputsEnabled =
transition.state === "idle" && !loaderData.smartList.archived;

return (
<LeafCard
key={loaderData.smartList.key.the_key}
showArchiveButton
enableArchiveButton={inputsEnabled}
returnLocation={`/workspace/smart-lists/${key}/items`}
>
<GlobalError actionResult={actionData} />
<Stack spacing={2}>
<FormControl fullWidth>
<InputLabel id="key">Key</InputLabel>
<OutlinedInput
label="Key"
name="key"
disabled
defaultValue={loaderData.smartList.key.the_key}
/>
</FormControl>

<FormControl fullWidth>
<InputLabel id="name">Name</InputLabel>
<OutlinedInput
label="Name"
name="name"
readOnly={!inputsEnabled}
defaultValue={loaderData.smartList.name.the_name}
/>
<FieldError actionResult={actionData} fieldName="/name" />
</FormControl>

<FormControl fullWidth>
<InputLabel id="icon">Icon</InputLabel>
<IconSelector
readOnly={!inputsEnabled}
defaultIcon={loaderData.smartList.icon?.the_icon}
/>
<FieldError actionResult={actionData} fieldName="/icon" />
</FormControl>
</Stack>

<Divider>Actions</Divider>

<ButtonGroup>
<Button
variant="contained"
disabled={!inputsEnabled}
type="submit"
name="intent"
value="update"
>
Save
</Button>
</ButtonGroup>
</LeafCard>
);
}

export function CatchBoundary() {
const caught = useCatch();
const { key } = useParams();

if (caught.status === StatusCodes.NOT_FOUND) {
return (
<article className="message is-warning">
<div className="message-header">
<p>Warning</p>
</div>
<div className="message-body">Could not find smart list #{key}!</div>
</article>
);
}

throw new Error(`Unhandled error: ${caught.status}`);
}

export function ErrorBoundary({ error }: { error: Error }) {
const globalProperties = useContext(GlobalPropertiesContext);

return (
<article className="message is-danger">
<div className="message-header">
<p>Danger</p>
</div>
<div className="message-body">
There was an error updating the smart list. Please try again!
</div>

{isDevelopment(globalProperties.env) && (
<div>
<pre>{error.message}</pre>
<pre>{error.stack}</pre>
</div>
)}
</article>
);
}
16 changes: 10 additions & 6 deletions src/webui/app/routes/workspace/vacations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { VacationService } from "jupiter-gen";

import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
import { Box, Button, Chip, IconButton, Stack } from "@mui/material";
import { Box, Button, Chip, IconButton, Stack, styled } from "@mui/material";
import type { CalendarTooltipProps, Datum } from "@nivo/calendar";
import { ResponsiveCalendar } from "@nivo/calendar";
import { DateTime } from "luxon";
Expand All @@ -23,7 +23,6 @@ import {
DisplayType,
useTrunkNeedsToShowLeaf,
} from "~/rendering/use-nested-entities";
import styles from "~/styles/vacations.css";

export const handle = {
displayType: DisplayType.TRUNK,
Expand All @@ -36,8 +35,6 @@ export async function loader() {
return json(response.vacations);
}

export const links = () => [{ rel: "stylesheet", href: styles }];

export default function Vacations() {
const vacations = useLoaderDataSafeForAnimation<typeof loader>();
const outlet = useOutlet();
Expand Down Expand Up @@ -191,7 +188,7 @@ function VacationCalendar({ sortedVacations }: VacationCalendarProps) {
}

return (
<div className="vacation-calendar">
<StyledDiv>
<IconButton
disabled={currentYear === firstYear}
onClick={handlePrevYear}
Expand Down Expand Up @@ -221,7 +218,7 @@ function VacationCalendar({ sortedVacations }: VacationCalendarProps) {
>
<ArrowForwardIosIcon fontSize="inherit" />
</IconButton>
</div>
</StyledDiv>
);
}

Expand All @@ -246,3 +243,10 @@ export function ErrorBoundary({ error }: { error: Error }) {
</article>
);
}

const StyledDiv = styled("div")`
height: 250px;
display: flex;
flex-direction: row;
`

Loading

0 comments on commit c8aac79

Please sign in to comment.