Skip to content

Commit 36293e9

Browse files
authored
Update Project Page Layout (#141)
* refactor(project-feedback): adjust layout to allow more feedback to be shown WIP * feature(project-feedback): add grid layout WIP * refactor(project): extract view toggle to client rendered component * fix(feedback-card): apply line clamp to title on project page * refactor(voting-buttons): adjust margin * refactor(switch-feedback-view): adjust placement and styles * refactor(comments): increase character limit * refactor(config): update copy
1 parent b3dead1 commit 36293e9

File tree

17 files changed

+380
-132
lines changed

17 files changed

+380
-132
lines changed

panda.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const pandaConfig = defineConfig({
2121
bottom: ["*"],
2222
right: ["*"],
2323
left: ["*"],
24+
colSpan: ["*"],
2425
},
2526
},
2627
],

src/components/feedback/CreateComment/CreateComment.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { toaster } from "lib/util";
2020

2121
import type { Session } from "next-auth";
2222

23-
const MAX_COMMENT_LENGTH = 240;
23+
const MAX_COMMENT_LENGTH = 500;
2424

2525
// TODO adjust schema in this file after closure on https://linear.app/omnidev/issue/OMNI-166/strategize-runtime-and-server-side-validation-approach and https://linear.app/omnidev/issue/OMNI-167/refine-validation-schemas
2626

src/components/feedback/CreateFeedback/CreateFeedback.tsx

Lines changed: 66 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { Stack, sigil } from "@omnidev/sigil";
3+
import { Collapsible, Stack, sigil } from "@omnidev/sigil";
44
import { useStore } from "@tanstack/react-form";
55
import { useQuery, useQueryClient } from "@tanstack/react-query";
66
import { useParams } from "next/navigation";
@@ -17,8 +17,10 @@ import {
1717
import { app } from "lib/config";
1818
import { DEBOUNCE_TIME, uuidSchema } from "lib/constants";
1919
import { useForm } from "lib/hooks";
20+
import { useDialogStore } from "lib/hooks/store";
2021
import { freeTierFeedbackOptions } from "lib/options";
2122
import { toaster } from "lib/util";
23+
import { DialogType } from "store";
2224

2325
import type { Session } from "next-auth";
2426

@@ -62,6 +64,10 @@ const CreateFeedback = ({ user }: Props) => {
6264
projectSlug: string;
6365
}>();
6466

67+
// TODO: discuss. Not technically a dialog, but acts similarly to add state management globally
68+
const { isOpen, setIsOpen } = useDialogStore({
69+
type: DialogType.CreateFeedback,
70+
});
6571
const { data: canCreateFeedback } = useQuery(
6672
freeTierFeedbackOptions({ organizationSlug, projectSlug }),
6773
);
@@ -165,63 +171,68 @@ const CreateFeedback = ({ user }: Props) => {
165171
);
166172

167173
return (
168-
<sigil.form
169-
display="flex"
170-
flexDirection="column"
171-
gap={2}
172-
onSubmit={async (e) => {
173-
e.preventDefault();
174-
e.stopPropagation();
175-
await handleSubmit();
174+
<Collapsible
175+
onOpenChange={({ open }) => {
176+
reset();
177+
setIsOpen(open);
176178
}}
179+
open={isOpen}
177180
>
178-
<AppField name="title">
179-
{({ InputField }) => (
180-
<InputField
181-
label={app.projectPage.projectFeedback.feedbackTitle.label}
182-
placeholder={
183-
app.projectPage.projectFeedback.feedbackTitle.placeholder
184-
}
185-
disabled={!user || !canCreateFeedback}
186-
tooltip={app.projectPage.projectFeedback.disabled}
187-
/>
188-
)}
189-
</AppField>
190-
191-
<AppField name="description">
192-
{({ TextareaField }) => (
193-
<TextareaField
194-
label={app.projectPage.projectFeedback.feedbackDescription.label}
195-
placeholder={
196-
app.projectPage.projectFeedback.feedbackDescription.placeholder
197-
}
198-
rows={5}
199-
minH={32}
200-
maxLength={MAX_DESCRIPTION_LENGTH}
201-
disabled={!user || !canCreateFeedback}
202-
tooltip={app.projectPage.projectFeedback.disabled}
181+
<sigil.form
182+
display="flex"
183+
flexDirection="column"
184+
gap={2}
185+
p={1}
186+
onSubmit={async (e) => {
187+
e.preventDefault();
188+
e.stopPropagation();
189+
await handleSubmit();
190+
}}
191+
>
192+
<AppField name="title">
193+
{({ InputField }) => (
194+
<InputField
195+
label={app.projectPage.projectFeedback.feedbackTitle.label}
196+
placeholder={
197+
app.projectPage.projectFeedback.feedbackTitle.placeholder
198+
}
199+
disabled={!user || !canCreateFeedback}
200+
/>
201+
)}
202+
</AppField>
203+
204+
<AppField name="description">
205+
{({ TextareaField }) => (
206+
<TextareaField
207+
label={app.projectPage.projectFeedback.feedbackDescription.label}
208+
placeholder={
209+
app.projectPage.projectFeedback.feedbackDescription.placeholder
210+
}
211+
rows={3}
212+
maxLength={MAX_DESCRIPTION_LENGTH}
213+
disabled={!user || !canCreateFeedback}
214+
/>
215+
)}
216+
</AppField>
217+
218+
<Stack justify="space-between" direction="row">
219+
<CharacterLimit
220+
value={descriptionLength}
221+
max={MAX_DESCRIPTION_LENGTH}
222+
placeSelf="flex-start"
203223
/>
204-
)}
205-
</AppField>
206-
207-
<Stack justify="space-between" direction="row">
208-
<CharacterLimit
209-
value={descriptionLength}
210-
max={MAX_DESCRIPTION_LENGTH}
211-
placeSelf="flex-start"
212-
/>
213-
214-
<AppForm>
215-
<SubmitForm
216-
action={app.projectPage.projectFeedback.action}
217-
isPending={isPending}
218-
w="fit-content"
219-
placeSelf="flex-end"
220-
disabled={!user || !canCreateFeedback}
221-
/>
222-
</AppForm>
223-
</Stack>
224-
</sigil.form>
224+
225+
<AppForm>
226+
<SubmitForm
227+
action={app.projectPage.projectFeedback.action}
228+
isPending={isPending}
229+
w="fit-content"
230+
placeSelf="flex-end"
231+
/>
232+
</AppForm>
233+
</Stack>
234+
</sigil.form>
235+
</Collapsible>
225236
);
226237
};
227238

src/components/feedback/FeedbackCard/FeedbackCard.tsx

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import { Format } from "@ark-ui/react";
34
import {
45
Circle,
56
HStack,
@@ -9,6 +10,7 @@ import {
910
MenuItemGroup,
1011
Stack,
1112
Text,
13+
sigil,
1214
} from "@omnidev/sigil";
1315
import { useQueryClient } from "@tanstack/react-query";
1416
import dayjs from "dayjs";
@@ -25,11 +27,11 @@ import {
2527
useStatusBreakdownQuery,
2628
useUpdatePostMutation,
2729
} from "generated/graphql";
30+
import { app } from "lib/config";
2831
import { useSearchParams } from "lib/hooks";
2932
import { useStatusMenuStore } from "lib/hooks/store";
3033

31-
import { Format } from "@ark-ui/react";
32-
import type { HstackProps } from "@omnidev/sigil";
34+
import type { HstackProps, TextProps } from "@omnidev/sigil";
3335
import type { InfiniteData } from "@tanstack/react-query";
3436
import type {
3537
FeedbackByIdQuery,
@@ -38,7 +40,6 @@ import type {
3840
PostStatus,
3941
PostsQuery,
4042
} from "generated/graphql";
41-
import { app } from "lib/config";
4243
import type { Session } from "next-auth";
4344

4445
interface ProjectStatus {
@@ -59,6 +60,10 @@ interface Props extends HstackProps {
5960
feedback: Partial<FeedbackFragment>;
6061
/** Project status options. */
6162
projectStatuses?: ProjectStatus[];
63+
/** Title props. */
64+
titleProps?: TextProps;
65+
/** Description props. */
66+
descriptionProps?: TextProps;
6267
}
6368

6469
/**
@@ -69,6 +74,8 @@ const FeedbackCard = ({
6974
canManageFeedback,
7075
feedback,
7176
projectStatuses,
77+
titleProps,
78+
descriptionProps,
7279
...rest
7380
}: Props) => {
7481
const { isStatusMenuOpen, setIsStatusMenuOpen } = useStatusMenuStore(
@@ -230,23 +237,23 @@ const FeedbackCard = ({
230237
return (
231238
<HStack
232239
position="relative"
233-
gap={8}
234240
bgColor="background.default"
235241
borderRadius="lg"
236-
p={{ base: 4, sm: 6 }}
242+
p={4}
237243
opacity={actionIsPending ? 0.5 : 1}
238244
{...rest}
239245
onClick={!isStatusMenuOpen ? rest.onClick : undefined}
240246
>
241-
<Stack w="full">
247+
<Stack h="full" w="full" gap={2}>
242248
<HStack justify="space-between">
243249
<Stack gap={1}>
244250
<Text
251+
wordBreak="break-word"
245252
fontWeight="semibold"
246253
fontSize="lg"
247-
lineHeight={1}
248254
// TODO: figure out container queries for this. The sizing feels off across different pages on both the projects page and feedback page
249255
maxW={{ base: "40svw", xl: "xl" }}
256+
{...titleProps}
250257
>
251258
{feedback.title}
252259
</Text>
@@ -284,8 +291,19 @@ const FeedbackCard = ({
284291
/>
285292
</HStack>
286293

287-
<Text wordBreak="break-word" color="foreground.muted">
288-
{feedback.description}
294+
<Text
295+
wordBreak="break-word"
296+
color="foreground.muted"
297+
flex={1}
298+
{...descriptionProps}
299+
>
300+
{feedback.description?.split("\n").map((line, index) => (
301+
// biome-ignore lint/suspicious/noArrayIndexKey: simple index due to the nature of the rendering
302+
<sigil.span key={index}>
303+
{line}
304+
<br />
305+
</sigil.span>
306+
))}
289307
</Text>
290308

291309
<Stack justify="space-between" gap={4} mt={2}>
@@ -344,16 +362,18 @@ const FeedbackCard = ({
344362
</MenuItemGroup>
345363
</Menu>
346364

347-
<Text
348-
display={{ base: "none", sm: "inline-flex" }}
349-
fontSize="sm"
350-
color="foreground.subtle"
351-
>
352-
{`Updated ${dayjs(isUpdateStatusPending ? new Date() : feedback.statusUpdatedAt).fromNow()}`}
353-
</Text>
365+
{isFeedbackRoute && (
366+
<Text
367+
display={{ base: "none", sm: "inline-flex" }}
368+
fontSize="sm"
369+
color="foreground.subtle"
370+
>
371+
{`Updated ${dayjs(isUpdateStatusPending ? new Date() : feedback.statusUpdatedAt).fromNow()}`}
372+
</Text>
373+
)}
354374
</HStack>
355375

356-
<HStack>
376+
<HStack mb={-2}>
357377
{canAdjustFeedback && (
358378
<HStack>
359379
<UpdateFeedback

src/components/feedback/VotingButtons/VotingButtons.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,13 @@ const VotingButtons = ({
7777
.otherwise(() => "brand.quinary");
7878

7979
return (
80-
<HStack gap={1} justify="center" placeSelf="flex-start" mr={-2.5} mt={-2}>
80+
<HStack
81+
gap={1}
82+
justify="center"
83+
placeSelf="flex-start"
84+
mr={-2.5}
85+
mt={isFeedbackRoute ? -2 : -1}
86+
>
8187
<Tooltip
8288
hasArrow={false}
8389
trigger={

src/components/layout/SectionContainer/SectionContainer.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { Flex, Icon, Stack, Text } from "@omnidev/sigil";
44

55
import type { FlexProps, TextProps } from "@omnidev/sigil";
6+
import type { ReactNode } from "react";
67
import type { IconType } from "react-icons";
78

89
interface Props extends FlexProps {
@@ -16,6 +17,8 @@ interface Props extends FlexProps {
1617
titleProps?: FlexProps;
1718
/** Additional props for the description container. */
1819
descriptionProps?: TextProps;
20+
/** Header actions. */
21+
headerActions?: ReactNode;
1922
}
2023

2124
/**
@@ -28,6 +31,7 @@ const SectionContainer = ({
2831
icon,
2932
titleProps,
3033
descriptionProps,
34+
headerActions,
3135
...rest
3236
}: Props) => (
3337
<Stack
@@ -40,16 +44,19 @@ const SectionContainer = ({
4044
{...rest}
4145
>
4246
<Stack>
43-
<Flex align="center" gap={2} {...titleProps}>
47+
<Flex
48+
align="center"
49+
gap={2}
50+
fontSize={{ base: "xl", lg: "2xl" }}
51+
{...titleProps}
52+
>
4453
{icon && <Icon src={icon} w={5} h={5} color="foreground.subtle" />}
4554

46-
<Text
47-
fontSize={{ base: "xl", lg: "2xl" }}
48-
fontWeight="semibold"
49-
lineHeight={1.2}
50-
>
55+
<Text fontWeight="semibold" lineHeight={1.2}>
5156
{title}
5257
</Text>
58+
59+
{headerActions && headerActions}
5360
</Flex>
5461

5562
{description && (

src/components/project/FeedbackMetrics/FeedbackMetrics.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,22 @@ const FeedbackMetrics = ({
4141
];
4242

4343
return (
44-
<SectionContainer title={app.projectPage.feedbackMetrics.title}>
44+
<SectionContainer
45+
title={app.projectPage.feedbackMetrics.title}
46+
titleProps={{ fontSize: "md" }}
47+
>
4548
{metrics.map(({ title, icon, value }) => (
4649
<Flex key={title} justify="space-between" align="center">
4750
<Flex gap={2} align="center">
4851
<Icon src={icon} />
4952

50-
<Text color="foreground.muted">{title}</Text>
53+
<Text color="foreground.muted" fontSize="sm">
54+
{title}
55+
</Text>
5156
</Flex>
5257

5358
<Skeleton isLoaded={isLoaded} minW={8}>
54-
<Text fontSize={{ base: "sm", lg: "md" }} textAlign="right">
59+
<Text fontSize="sm" textAlign="right">
5560
{isError ? 0 : value}
5661
</Text>
5762
</Skeleton>

0 commit comments

Comments
 (0)