mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-10-03 18:23:47 +10:00
feat(web): stack entry fields in one column and let dates fill the row
The write panel is narrow, so an entry's fields now take the whole column. Start and end dates still share a line and now span it, with Present below. A new entry's badge just says Draft.
This commit is contained in:
@@ -49,7 +49,7 @@ function MonthYearInput({
|
||||
const formatted = value ? formatYearMonth(value, { locale, format }) : "";
|
||||
|
||||
return (
|
||||
<div className="relative w-[132px]">
|
||||
<div className="relative min-w-0 flex-1">
|
||||
<Icon
|
||||
name="calendar_month"
|
||||
size={18}
|
||||
@@ -109,7 +109,7 @@ export function DatesField({ dates, single = false, locale, format, onChange, cl
|
||||
{single ? <Trans>Date</Trans> : <Trans>Dates</Trans>}
|
||||
</legend>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<MonthYearInput
|
||||
label={single ? t`Date` : t`Start`}
|
||||
value={dates.start}
|
||||
@@ -134,18 +134,23 @@ export function DatesField({ dates, single = false, locale, format, onChange, cl
|
||||
onCommit={(end) => onChange({ ...current, end })}
|
||||
onInvalid={setInvalidFor("end")}
|
||||
/>
|
||||
{/* biome-ignore lint/a11y/noLabelWithoutControl: Base UI's Switch is the control; wrapping it in a label is its documented pattern. */}
|
||||
<label className="flex cursor-pointer items-center gap-2 p-1 text-sm">
|
||||
<Switch
|
||||
checked={dates.present}
|
||||
onCheckedChange={(present) => onChange({ ...current, present, end: present ? null : dates.end })}
|
||||
/>
|
||||
<Trans>Present</Trans>
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!single && (
|
||||
<>
|
||||
{/* biome-ignore lint/a11y/noLabelWithoutControl: Base UI's Switch is the control; wrapping it in a label is its documented pattern. */}
|
||||
<label className="flex w-fit cursor-pointer items-center gap-2 p-1 text-sm">
|
||||
<Switch
|
||||
checked={dates.present}
|
||||
onCheckedChange={(present) => onChange({ ...current, present, end: present ? null : dates.end })}
|
||||
/>
|
||||
<Trans>Present</Trans>
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div id={`${id}-note`}>
|
||||
{hasError ? (
|
||||
<p className="flex items-start gap-1 text-danger-text text-xs leading-4">
|
||||
|
||||
@@ -142,7 +142,7 @@ export function EntryCard({ section, entryId, index, count, page, locked, onMove
|
||||
</span>
|
||||
{draft && (
|
||||
<Badge variant="neutral" className="shrink-0">
|
||||
<Trans>Draft · not printed</Trans>
|
||||
<Trans>Draft</Trans>
|
||||
</Badge>
|
||||
)}
|
||||
{entry.hidden && (
|
||||
@@ -171,7 +171,7 @@ export function EntryCard({ section, entryId, index, count, page, locked, onMove
|
||||
<CollapsibleContent>
|
||||
<fieldset
|
||||
disabled={locked}
|
||||
className="m-0 grid min-w-0 grid-cols-2 gap-x-3 gap-y-2.5 border-0 border-line border-t px-3 pt-3 pb-3.5"
|
||||
className="m-0 grid min-w-0 gap-y-2.5 border-0 border-line border-t px-3 pt-3 pb-3.5"
|
||||
>
|
||||
{fields}
|
||||
</fieldset>
|
||||
@@ -237,7 +237,7 @@ function PhoneEntryScreen({ section, title, locked, onBack, onDelete, children }
|
||||
</div>
|
||||
<fieldset
|
||||
disabled={locked}
|
||||
className="m-0 grid min-w-0 flex-1 auto-rows-min grid-cols-2 gap-x-3 gap-y-3 overflow-y-auto border-0 p-4 [&_input]:h-11"
|
||||
className="m-0 grid min-w-0 flex-1 auto-rows-min gap-y-3 overflow-y-auto border-0 p-4 [&_input]:h-11"
|
||||
>
|
||||
{children}
|
||||
</fieldset>
|
||||
|
||||
@@ -167,7 +167,7 @@ export function MoreOptions({ children }: MoreOptionsProps) {
|
||||
<Trans>More options</Trans>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent hiddenUntilFound>
|
||||
<div className="grid grid-cols-2 gap-x-3 gap-y-2.5 pt-3">{children}</div>
|
||||
<div className="grid gap-y-2.5 pt-3">{children}</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
|
||||
@@ -95,12 +95,12 @@ describe("WritePanel", () => {
|
||||
|
||||
fireEvent.click(within(experience).getByRole("button", { name: "Add experience" }));
|
||||
|
||||
await waitFor(() => expect(within(experience).getByText("Draft · not printed")).toBeInTheDocument());
|
||||
await waitFor(() => expect(within(experience).getByText("Draft", { exact: true })).toBeInTheDocument());
|
||||
expect(document.activeElement).toHaveAttribute("data-entry-field", "position");
|
||||
|
||||
fireEvent.change(within(experience).getByRole("textbox", { name: "Company" }), { target: { value: "Lumen" } });
|
||||
|
||||
expect(within(experience).queryByText("Draft · not printed")).not.toBeInTheDocument();
|
||||
expect(within(experience).queryByText("Draft", { exact: true })).not.toBeInTheDocument();
|
||||
expect(data().sections.experience.items.at(-1)).toMatchObject({ company: "Lumen" });
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ test("adds an experience item and persists it across reloads", async ({ authPage
|
||||
|
||||
// The new draft opens in place with its first field focused; it saves as you type.
|
||||
const entry = page.locator("#sidebar-experience [data-entry-id]").last();
|
||||
await expect(entry.getByText("Draft · not printed")).toBeVisible();
|
||||
await expect(entry.getByText("Draft", { exact: true })).toBeVisible();
|
||||
await expect(entry.getByRole("textbox", { name: "Position" })).toBeFocused();
|
||||
|
||||
const savePromise = page.waitForResponse((response) => {
|
||||
@@ -26,7 +26,7 @@ test("adds an experience item and persists it across reloads", async ({ authPage
|
||||
await savePromise;
|
||||
|
||||
// With a company it's no longer a draft, and its card reads "company · …"
|
||||
await expect(entry.getByText("Draft · not printed")).toHaveCount(0);
|
||||
await expect(entry.getByText("Draft", { exact: true })).toHaveCount(0);
|
||||
await expect(page.getByText(company).filter({ visible: true }).first()).toBeVisible();
|
||||
|
||||
// And it survives a full reload
|
||||
|
||||
Reference in New Issue
Block a user