@rapidmx/web-client 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +382 -0
- package/apps/admin/_404.tsx +9 -0
- package/apps/admin/_500.tsx +9 -0
- package/apps/admin/_layout.tsx +41 -0
- package/apps/admin/audit-log/index.tsx +187 -0
- package/apps/admin/branding/index.tsx +404 -0
- package/apps/admin/distribution-lists/[uid].tsx +70 -0
- package/apps/admin/distribution-lists/index.tsx +115 -0
- package/apps/admin/distribution-lists/new/index.tsx +114 -0
- package/apps/admin/domains/[uid].tsx +259 -0
- package/apps/admin/domains/index.tsx +125 -0
- package/apps/admin/domains/new/index.tsx +84 -0
- package/apps/admin/index.tsx +82 -0
- package/apps/admin/ingest-queue/index.tsx +116 -0
- package/apps/admin/mailboxes/[uid].tsx +126 -0
- package/apps/admin/mailboxes/new/index.tsx +291 -0
- package/apps/admin/quarantine/index.tsx +171 -0
- package/apps/admin/transport-rules/[uid].tsx +143 -0
- package/apps/admin/transport-rules/_transportRuleConfig.tsx +75 -0
- package/apps/admin/transport-rules/index.tsx +117 -0
- package/apps/admin/transport-rules/new/index.tsx +105 -0
- package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +117 -0
- package/apps/shared/components/admin/layout/AdminShell.tsx +186 -0
- package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +73 -0
- package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +154 -0
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +124 -0
- package/apps/shared/components/booking/AvailabilityEditor.tsx +118 -0
- package/apps/shared/components/calendar/CalendarListSidebar.tsx +119 -0
- package/apps/shared/components/calendar/EventModal.tsx +513 -0
- package/apps/shared/components/calendar/MonthView.tsx +139 -0
- package/apps/shared/components/calendar/RecurrenceEditor.tsx +180 -0
- package/apps/shared/components/calendar/ResourcePicker.tsx +80 -0
- package/apps/shared/components/calendar/SplitDayView.tsx +137 -0
- package/apps/shared/components/calendar/TimeGridView.tsx +198 -0
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +211 -0
- package/apps/shared/components/contacts/ContactDetailPane.tsx +99 -0
- package/apps/shared/components/contacts/ContactForm.tsx +314 -0
- package/apps/shared/components/contacts/ContactsSidebar.tsx +242 -0
- package/apps/shared/components/contacts/ContactsToolbar.tsx +115 -0
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +197 -0
- package/apps/shared/components/layout/AppShell.tsx +165 -0
- package/apps/shared/components/layout/BrandingChrome.tsx +25 -0
- package/apps/shared/components/layout/MailboxProvisioning.tsx +110 -0
- package/apps/shared/components/layout/UserMenu.tsx +150 -0
- package/apps/shared/components/mail/ConversationList.tsx +61 -0
- package/apps/shared/components/mail/ConversationThreadPane.tsx +174 -0
- package/apps/shared/components/mail/MessageDetailPane.tsx +390 -0
- package/apps/shared/components/mail/compose/ComposeContext.tsx +114 -0
- package/apps/shared/components/mail/compose/ComposeToolbar.tsx +418 -0
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +518 -0
- package/apps/shared/components/mail/compose/EmojiPicker.tsx +64 -0
- package/apps/shared/components/mail/compose/GifPicker.tsx +82 -0
- package/apps/shared/components/mail/compose/RichTextEditor.tsx +83 -0
- package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +64 -0
- package/apps/shared/components/mail/layout/MailShell.tsx +287 -0
- package/apps/shared/components/rules/RuleBuilder.tsx +276 -0
- package/apps/shared/components/settings/layout/SettingsShell.tsx +223 -0
- package/apps/shared/components/tasks/TasksSidebar.tsx +201 -0
- package/apps/shared/components/tasks/TasksToolbar.tsx +68 -0
- package/apps/shared/components/tasks/layout/TasksShell.tsx +199 -0
- package/apps/shared/styles/app.css +265 -0
- package/apps/www/_404.tsx +9 -0
- package/apps/www/_500.tsx +9 -0
- package/apps/www/_layout.tsx +45 -0
- package/apps/www/calendar/index.tsx +407 -0
- package/apps/www/contacts/[uid].tsx +88 -0
- package/apps/www/contacts/index.tsx +436 -0
- package/apps/www/index.tsx +375 -0
- package/apps/www/messages/[uid].tsx +70 -0
- package/apps/www/settings/auto-reply/index.tsx +129 -0
- package/apps/www/settings/booking-types/[uid].tsx +300 -0
- package/apps/www/settings/booking-types/index.tsx +98 -0
- package/apps/www/settings/booking-types/new/index.tsx +212 -0
- package/apps/www/settings/filters/[uid].tsx +155 -0
- package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +102 -0
- package/apps/www/settings/filters/index.tsx +99 -0
- package/apps/www/settings/filters/new/index.tsx +126 -0
- package/apps/www/settings/focused-inbox/index.tsx +148 -0
- package/apps/www/settings/read-receipts/index.tsx +125 -0
- package/apps/www/settings/signatures/[uid].tsx +167 -0
- package/apps/www/settings/signatures/index.tsx +85 -0
- package/apps/www/settings/signatures/new/index.tsx +129 -0
- package/apps/www/settings/signatures/signatureDefaults.ts +36 -0
- package/apps/www/tasks/index.tsx +530 -0
- package/dist/apps/admin/_404.js +4 -0
- package/dist/apps/admin/_500.js +4 -0
- package/dist/apps/admin/_layout.js +10 -0
- package/dist/apps/admin/audit-log/index.js +66 -0
- package/dist/apps/admin/branding/index.js +126 -0
- package/dist/apps/admin/distribution-lists/[uid].js +34 -0
- package/dist/apps/admin/distribution-lists/index.js +31 -0
- package/dist/apps/admin/distribution-lists/new/index.js +51 -0
- package/dist/apps/admin/domains/[uid].js +102 -0
- package/dist/apps/admin/domains/index.js +31 -0
- package/dist/apps/admin/domains/new/index.js +41 -0
- package/dist/apps/admin/index.js +32 -0
- package/dist/apps/admin/ingest-queue/index.js +48 -0
- package/dist/apps/admin/mailboxes/[uid].js +59 -0
- package/dist/apps/admin/mailboxes/new/index.js +93 -0
- package/dist/apps/admin/quarantine/index.js +66 -0
- package/dist/apps/admin/transport-rules/[uid].js +87 -0
- package/dist/apps/admin/transport-rules/_transportRuleConfig.js +37 -0
- package/dist/apps/admin/transport-rules/index.js +32 -0
- package/dist/apps/admin/transport-rules/new/index.js +50 -0
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +64 -0
- package/dist/apps/shared/components/admin/layout/AdminShell.js +94 -0
- package/dist/apps/shared/components/admin/mailboxes/MailboxTable.js +16 -0
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +59 -0
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +67 -0
- package/dist/apps/shared/components/booking/AvailabilityEditor.js +45 -0
- package/dist/apps/shared/components/calendar/CalendarListSidebar.js +44 -0
- package/dist/apps/shared/components/calendar/EventModal.js +167 -0
- package/dist/apps/shared/components/calendar/MonthView.js +38 -0
- package/dist/apps/shared/components/calendar/RecurrenceEditor.js +55 -0
- package/dist/apps/shared/components/calendar/ResourcePicker.js +36 -0
- package/dist/apps/shared/components/calendar/SplitDayView.js +44 -0
- package/dist/apps/shared/components/calendar/TimeGridView.js +58 -0
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +96 -0
- package/dist/apps/shared/components/contacts/ContactDetailPane.js +11 -0
- package/dist/apps/shared/components/contacts/ContactForm.js +85 -0
- package/dist/apps/shared/components/contacts/ContactsSidebar.js +105 -0
- package/dist/apps/shared/components/contacts/ContactsToolbar.js +32 -0
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +93 -0
- package/dist/apps/shared/components/layout/AppShell.js +68 -0
- package/dist/apps/shared/components/layout/BrandingChrome.js +18 -0
- package/dist/apps/shared/components/layout/MailboxProvisioning.js +75 -0
- package/dist/apps/shared/components/layout/UserMenu.js +59 -0
- package/dist/apps/shared/components/mail/ConversationList.js +17 -0
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +112 -0
- package/dist/apps/shared/components/mail/MessageDetailPane.js +153 -0
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +55 -0
- package/dist/apps/shared/components/mail/compose/ComposeToolbar.js +88 -0
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +261 -0
- package/dist/apps/shared/components/mail/compose/EmojiPicker.js +25 -0
- package/dist/apps/shared/components/mail/compose/GifPicker.js +36 -0
- package/dist/apps/shared/components/mail/compose/RichTextEditor.js +60 -0
- package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +33 -0
- package/dist/apps/shared/components/mail/layout/MailShell.js +139 -0
- package/dist/apps/shared/components/rules/RuleBuilder.js +78 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +100 -0
- package/dist/apps/shared/components/tasks/TasksSidebar.js +81 -0
- package/dist/apps/shared/components/tasks/TasksToolbar.js +15 -0
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +92 -0
- package/dist/apps/shared/styles/app.css +265 -0
- package/dist/apps/www/_404.js +4 -0
- package/dist/apps/www/_500.js +4 -0
- package/dist/apps/www/_layout.js +8 -0
- package/dist/apps/www/calendar/index.js +228 -0
- package/dist/apps/www/contacts/[uid].js +62 -0
- package/dist/apps/www/contacts/index.js +262 -0
- package/dist/apps/www/index.js +228 -0
- package/dist/apps/www/messages/[uid].js +47 -0
- package/dist/apps/www/settings/auto-reply/index.js +54 -0
- package/dist/apps/www/settings/booking-types/[uid].js +129 -0
- package/dist/apps/www/settings/booking-types/index.js +29 -0
- package/dist/apps/www/settings/booking-types/new/index.js +86 -0
- package/dist/apps/www/settings/filters/[uid].js +94 -0
- package/dist/apps/www/settings/filters/_mailFilterRuleConfig.js +65 -0
- package/dist/apps/www/settings/filters/index.js +30 -0
- package/dist/apps/www/settings/filters/new/index.js +61 -0
- package/dist/apps/www/settings/focused-inbox/index.js +69 -0
- package/dist/apps/www/settings/read-receipts/index.js +51 -0
- package/dist/apps/www/settings/signatures/[uid].js +96 -0
- package/dist/apps/www/settings/signatures/index.js +34 -0
- package/dist/apps/www/settings/signatures/new/index.js +63 -0
- package/dist/apps/www/settings/signatures/signatureDefaults.js +30 -0
- package/dist/apps/www/tasks/index.js +280 -0
- package/package.json +87 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
import React, { useState } from "react";
|
|
6
|
+
import { BookingAvailabilityWindow } from "@rapidmx/react-shared/booking/bookingApi.js";
|
|
7
|
+
import Button from "@rapidmx/react-shared/components/buttons/Button.js";
|
|
8
|
+
|
|
9
|
+
const DAY_LABELS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
10
|
+
const INPUT_CLASS =
|
|
11
|
+
"text-sm py-2 px-3 border border-border rounded-sm bg-surface text-text focus:outline-none focus:border-primary";
|
|
12
|
+
|
|
13
|
+
function minutesToTimeValue(minutes: number): string {
|
|
14
|
+
const h = Math.floor(minutes / 60)
|
|
15
|
+
.toString()
|
|
16
|
+
.padStart(2, "0");
|
|
17
|
+
const m = (minutes % 60).toString().padStart(2, "0");
|
|
18
|
+
return `${h}:${m}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function timeValueToMinutes(value: string): number {
|
|
22
|
+
const [h, m] = value.split(":").map(Number);
|
|
23
|
+
return h * 60 + m;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AvailabilityEditorProps {
|
|
27
|
+
value: BookingAvailabilityWindow[];
|
|
28
|
+
onChange: (value: BookingAvailabilityWindow[]) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Adds/removes `BookingType.availability` weekly windows — deliberately does not cover `dateOverrides`
|
|
33
|
+
* (per-date blackouts/exceptions), a separate, less commonly needed escape hatch left for a future pass;
|
|
34
|
+
* `BookingUtils.generateCandidateSlots()` already handles an empty `dateOverrides` array gracefully.
|
|
35
|
+
*/
|
|
36
|
+
export default function AvailabilityEditor({ value, onChange }: AvailabilityEditorProps) {
|
|
37
|
+
const [dayOfWeek, setDayOfWeek] = useState(1);
|
|
38
|
+
const [startTime, setStartTime] = useState("09:00");
|
|
39
|
+
const [endTime, setEndTime] = useState("17:00");
|
|
40
|
+
const [error, setError] = useState<string | null>(null);
|
|
41
|
+
|
|
42
|
+
function handleAdd() {
|
|
43
|
+
const startMinute = timeValueToMinutes(startTime);
|
|
44
|
+
const endMinute = timeValueToMinutes(endTime);
|
|
45
|
+
if (startMinute >= endMinute) {
|
|
46
|
+
setError("Start time must be before end time.");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
setError(null);
|
|
50
|
+
onChange([...value, { dayOfWeek, startMinute, endMinute }]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function handleRemove(index: number) {
|
|
54
|
+
onChange(value.filter((_, i) => i !== index));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className="flex flex-col gap-3">
|
|
59
|
+
{error && <p className="text-sm text-danger">{error}</p>}
|
|
60
|
+
{value.length === 0 ? (
|
|
61
|
+
<p className="text-sm text-text-muted">No availability windows yet — add one below.</p>
|
|
62
|
+
) : (
|
|
63
|
+
<ul className="flex flex-col gap-2">
|
|
64
|
+
{value.map((window, index) => (
|
|
65
|
+
<li
|
|
66
|
+
key={index}
|
|
67
|
+
className="flex items-center justify-between gap-3 py-2 px-3 border border-border rounded-sm text-sm"
|
|
68
|
+
>
|
|
69
|
+
<span>
|
|
70
|
+
{DAY_LABELS[window.dayOfWeek]} {minutesToTimeValue(window.startMinute)}–
|
|
71
|
+
{minutesToTimeValue(window.endMinute)}
|
|
72
|
+
</span>
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
onClick={() => handleRemove(index)}
|
|
76
|
+
className="text-sm text-danger hover:underline"
|
|
77
|
+
>
|
|
78
|
+
Remove
|
|
79
|
+
</button>
|
|
80
|
+
</li>
|
|
81
|
+
))}
|
|
82
|
+
</ul>
|
|
83
|
+
)}
|
|
84
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
85
|
+
<select
|
|
86
|
+
aria-label="Day of week"
|
|
87
|
+
value={dayOfWeek}
|
|
88
|
+
onChange={(e) => setDayOfWeek(Number(e.target.value))}
|
|
89
|
+
className={INPUT_CLASS}
|
|
90
|
+
>
|
|
91
|
+
{DAY_LABELS.map((label, i) => (
|
|
92
|
+
<option key={label} value={i}>
|
|
93
|
+
{label}
|
|
94
|
+
</option>
|
|
95
|
+
))}
|
|
96
|
+
</select>
|
|
97
|
+
<input
|
|
98
|
+
type="time"
|
|
99
|
+
aria-label="Start time"
|
|
100
|
+
value={startTime}
|
|
101
|
+
onChange={(e) => setStartTime(e.target.value)}
|
|
102
|
+
className={INPUT_CLASS}
|
|
103
|
+
/>
|
|
104
|
+
<span className="text-sm text-text-muted">to</span>
|
|
105
|
+
<input
|
|
106
|
+
type="time"
|
|
107
|
+
aria-label="End time"
|
|
108
|
+
value={endTime}
|
|
109
|
+
onChange={(e) => setEndTime(e.target.value)}
|
|
110
|
+
className={INPUT_CLASS}
|
|
111
|
+
/>
|
|
112
|
+
<Button type="button" variant="secondary" className="!w-auto" onClick={handleAdd}>
|
|
113
|
+
Add window
|
|
114
|
+
</Button>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
import React, { FormEvent, useState } from "react";
|
|
6
|
+
import { CALENDAR_COLOR_PALETTE, colorForFolder } from "@rapidmx/react-shared/calendar/calendarColors.js";
|
|
7
|
+
import { Folder } from "@rapidmx/react-shared/mail/mailApi.js";
|
|
8
|
+
import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
|
|
9
|
+
|
|
10
|
+
export interface CalendarListSidebarProps {
|
|
11
|
+
/** Every `type === "calendar"` folder for the currently-resolved mailbox. */
|
|
12
|
+
calendars: Folder[];
|
|
13
|
+
checkedFolderUids: Set<string>;
|
|
14
|
+
onToggle: (folderUid: string) => void;
|
|
15
|
+
onAddCalendar: (name: string, color: string) => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Calendar's left-sidebar checklist of "My calendars" — one row per `calendar`-type folder, a color
|
|
20
|
+
* swatch matching `colorForFolder`, and a checkbox controlling whether that calendar's events are
|
|
21
|
+
* currently shown (the checked set is owned by `apps/www/calendar/index.tsx`, since it drives which
|
|
22
|
+
* folders get fanned out to on every reload). "+ Add calendar" is a real `createFolder({type:
|
|
23
|
+
* "calendar", ...})` call — see the Phase 4 plan's own note that folder creation has no type
|
|
24
|
+
* restriction server-side, so this needed no new backend route.
|
|
25
|
+
*/
|
|
26
|
+
export default function CalendarListSidebar({ calendars, checkedFolderUids, onToggle, onAddCalendar }: CalendarListSidebarProps) {
|
|
27
|
+
const [adding, setAdding] = useState(false);
|
|
28
|
+
const [name, setName] = useState("");
|
|
29
|
+
const [color, setColor] = useState(CALENDAR_COLOR_PALETTE[0]);
|
|
30
|
+
const [error, setError] = useState<string | null>(null);
|
|
31
|
+
const [saving, setSaving] = useState(false);
|
|
32
|
+
|
|
33
|
+
async function handleAdd(e: FormEvent) {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
if (!name.trim()) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
setError(null);
|
|
39
|
+
setSaving(true);
|
|
40
|
+
try {
|
|
41
|
+
await onAddCalendar(name.trim(), color);
|
|
42
|
+
setName("");
|
|
43
|
+
setColor(CALENDAR_COLOR_PALETTE[0]);
|
|
44
|
+
setAdding(false);
|
|
45
|
+
} catch {
|
|
46
|
+
setError("Could not create this calendar.");
|
|
47
|
+
} finally {
|
|
48
|
+
setSaving(false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<nav aria-label="My calendars" className="p-3 border-t border-border">
|
|
54
|
+
<div className="flex items-center justify-between mb-1">
|
|
55
|
+
<h2 className="text-xs font-bold uppercase tracking-wide text-text-muted">My calendars</h2>
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
onClick={() => setAdding(true)}
|
|
59
|
+
aria-label="Add calendar"
|
|
60
|
+
className="text-xs font-bold text-primary-dark hover:underline"
|
|
61
|
+
>
|
|
62
|
+
+
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
{error && (
|
|
66
|
+
<div className="mb-1">
|
|
67
|
+
<Alert>{error}</Alert>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
<div className="flex flex-col gap-0.5">
|
|
71
|
+
{calendars.map((folder) => (
|
|
72
|
+
<label key={folder.uid} className="flex items-center gap-2 text-sm px-1 py-1 rounded-sm hover:bg-surface-alt cursor-pointer">
|
|
73
|
+
<input
|
|
74
|
+
type="checkbox"
|
|
75
|
+
checked={checkedFolderUids.has(folder.uid)}
|
|
76
|
+
onChange={() => onToggle(folder.uid)}
|
|
77
|
+
style={{ accentColor: colorForFolder(folder) }}
|
|
78
|
+
/>
|
|
79
|
+
<span aria-hidden="true" className="w-2.5 h-2.5 rounded-full shrink-0" style={{ backgroundColor: colorForFolder(folder) }} />
|
|
80
|
+
<span className="truncate">{folder.name}</span>
|
|
81
|
+
</label>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
{adding && (
|
|
85
|
+
<form onSubmit={handleAdd} className="flex flex-col gap-1.5 mt-2">
|
|
86
|
+
<input
|
|
87
|
+
type="text"
|
|
88
|
+
autoFocus
|
|
89
|
+
aria-label="New calendar name"
|
|
90
|
+
value={name}
|
|
91
|
+
onChange={(e) => setName(e.target.value)}
|
|
92
|
+
className="text-xs py-1 px-1.5 border border-border rounded-sm bg-surface"
|
|
93
|
+
/>
|
|
94
|
+
<div className="flex items-center gap-1" role="radiogroup" aria-label="Calendar color">
|
|
95
|
+
{CALENDAR_COLOR_PALETTE.map((swatch) => (
|
|
96
|
+
<button
|
|
97
|
+
key={swatch}
|
|
98
|
+
type="button"
|
|
99
|
+
aria-label={`Color ${swatch}`}
|
|
100
|
+
aria-pressed={color === swatch}
|
|
101
|
+
onClick={() => setColor(swatch)}
|
|
102
|
+
className={["w-4 h-4 rounded-full shrink-0", color === swatch ? "ring-2 ring-offset-1 ring-text" : ""].join(" ")}
|
|
103
|
+
style={{ backgroundColor: swatch }}
|
|
104
|
+
/>
|
|
105
|
+
))}
|
|
106
|
+
</div>
|
|
107
|
+
<div className="flex items-center gap-2">
|
|
108
|
+
<button type="submit" disabled={saving} className="text-xs font-semibold text-primary-dark">
|
|
109
|
+
Add
|
|
110
|
+
</button>
|
|
111
|
+
<button type="button" onClick={() => setAdding(false)} className="text-xs text-text-muted">
|
|
112
|
+
Cancel
|
|
113
|
+
</button>
|
|
114
|
+
</div>
|
|
115
|
+
</form>
|
|
116
|
+
)}
|
|
117
|
+
</nav>
|
|
118
|
+
);
|
|
119
|
+
}
|