Skip to content

Commit b97e378

Browse files
authored
Merge pull request #348 from datum-cloud/feat/domain-notes
feat: add notes to domain detail view
2 parents eb2a04a + d1adb8f commit b97e378

File tree

15 files changed

+2927
-29
lines changed

15 files changed

+2927
-29
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Textarea } from '@/modules/shadcn/ui/textarea';
2+
import { noteCreateMutation } from '@/resources/request/client';
3+
import { Button } from '@datum-ui/button';
4+
import { toast } from '@datum-ui/toast';
5+
import { Text } from '@datum-ui/typography';
6+
import { Trans, useLingui } from '@lingui/react/macro';
7+
import { useState } from 'react';
8+
9+
interface CreateNoteFormProps {
10+
projectName: string;
11+
namespace: string;
12+
domainName: string;
13+
onCreated: () => void;
14+
}
15+
16+
export function CreateNoteForm({
17+
projectName,
18+
namespace,
19+
domainName,
20+
onCreated,
21+
}: CreateNoteFormProps) {
22+
const { t } = useLingui();
23+
const [content, setContent] = useState('');
24+
const [isSubmitting, setIsSubmitting] = useState(false);
25+
26+
const handleSubmit = async (e: React.FormEvent) => {
27+
e.preventDefault();
28+
setIsSubmitting(true);
29+
try {
30+
await noteCreateMutation(projectName, namespace, domainName, content.trim());
31+
setContent('');
32+
onCreated();
33+
toast.success(t`Note added`);
34+
} catch {
35+
// Error toast is handled by the axios interceptor
36+
} finally {
37+
setIsSubmitting(false);
38+
}
39+
};
40+
41+
return (
42+
<form onSubmit={handleSubmit} className="flex flex-col gap-2">
43+
<label htmlFor="note-content">
44+
<Text>
45+
<Trans>Add a note</Trans>
46+
</Text>
47+
</label>
48+
<Textarea
49+
id="note-content"
50+
value={content}
51+
onChange={(e) => setContent(e.target.value)}
52+
placeholder={t`Enter note content...`}
53+
rows={4}
54+
disabled={isSubmitting}
55+
/>
56+
<div className="flex items-center justify-between">
57+
<Text size="xs" textColor={content.length > 1000 ? 'destructive' : 'muted'}>
58+
{content.length}/1000
59+
</Text>
60+
<Button
61+
htmlType="submit"
62+
disabled={content.trim().length === 0 || content.length > 1000 || isSubmitting}
63+
loading={isSubmitting}>
64+
<Trans>Add Note</Trans>
65+
</Button>
66+
</div>
67+
</form>
68+
);
69+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import ButtonDeleteAction from '@/components/button/button-delete-action';
2+
import { DateTime } from '@/components/date';
3+
import { noteDeleteMutation } from '@/resources/request/client';
4+
import { toast } from '@datum-ui/toast';
5+
import { Text } from '@datum-ui/typography';
6+
import { Trans, useLingui } from '@lingui/react/macro';
7+
import { ComMiloapisNotesV1Alpha1Note } from '@openapi/notes.miloapis.com/v1alpha1';
8+
9+
interface NoteCardProps {
10+
note: ComMiloapisNotesV1Alpha1Note;
11+
projectName: string;
12+
namespace: string;
13+
creatorEmail?: string;
14+
onDeleted: () => void;
15+
}
16+
17+
export function NoteCard({ note, projectName, namespace, creatorEmail, onDeleted }: NoteCardProps) {
18+
const { t } = useLingui();
19+
20+
const noteName = note.metadata?.name;
21+
22+
const handleDelete = async () => {
23+
if (!noteName) return;
24+
try {
25+
await noteDeleteMutation(projectName, namespace, noteName);
26+
toast.success(t`Note deleted`);
27+
onDeleted();
28+
} catch {
29+
// Error toast is handled by the axios interceptor
30+
}
31+
};
32+
33+
return (
34+
<div className="flex items-start justify-between gap-4">
35+
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
36+
<Text className="break-words whitespace-pre-wrap">{note.spec?.content}</Text>
37+
<Text size="xs" textColor="muted">
38+
<Trans>Added by</Trans> {creatorEmail ?? note.spec?.creatorRef?.name ?? t`Unknown`}
39+
{' · '}
40+
<DateTime date={note.metadata?.creationTimestamp} variant="both" />
41+
</Text>
42+
</div>
43+
<ButtonDeleteAction
44+
itemType={t`Note`}
45+
description={t`This note will be permanently deleted and cannot be recovered.`}
46+
onConfirm={handleDelete}
47+
buttonProps={{ disabled: !noteName }}
48+
/>
49+
</div>
50+
);
51+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { NoteCard } from './note-card';
2+
import { Text } from '@datum-ui/typography';
3+
import { Trans } from '@lingui/react/macro';
4+
import { ComMiloapisNotesV1Alpha1NoteList } from '@openapi/notes.miloapis.com/v1alpha1';
5+
6+
interface NotesListProps {
7+
notes: ComMiloapisNotesV1Alpha1NoteList | null | undefined;
8+
projectName: string;
9+
namespace: string;
10+
userEmails?: Record<string, string>;
11+
onNoteDeleted: () => void;
12+
}
13+
14+
export function NotesList({
15+
notes,
16+
projectName,
17+
namespace,
18+
userEmails,
19+
onNoteDeleted,
20+
}: NotesListProps) {
21+
const sorted = [...(notes?.items ?? [])].sort(
22+
(a, b) =>
23+
new Date(b.metadata?.creationTimestamp ?? 0).getTime() -
24+
new Date(a.metadata?.creationTimestamp ?? 0).getTime()
25+
);
26+
27+
if (sorted.length === 0) {
28+
return (
29+
<Text textColor="muted">
30+
<Trans>No notes yet. Add the first note below.</Trans>
31+
</Text>
32+
);
33+
}
34+
35+
return (
36+
<div className="divide-y">
37+
{sorted.map((note) => (
38+
<div key={note.metadata?.name} className="py-2 first:pt-0 last:pb-0">
39+
<NoteCard
40+
note={note}
41+
projectName={projectName}
42+
namespace={namespace}
43+
creatorEmail={
44+
note.spec?.creatorRef?.name ? userEmails?.[note.spec.creatorRef.name] : undefined
45+
}
46+
onDeleted={onNoteDeleted}
47+
/>
48+
</div>
49+
))}
50+
</div>
51+
);
52+
}

app/features/domain/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ export * from './hooks/useDomainStatus';
22
export * from './components/domain-status-probe';
33
export * from './components/domain-expiration';
44
export * from './components/domain-dns-provider';
5+
export * from './components/note-card';
6+
export * from './components/notes-list';
7+
export * from './components/create-note-form';

app/modules/i18n/locales/en.po

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ msgstr "Activity"
8484
msgid "Add"
8585
msgstr "Add"
8686

87+
#: app/features/domain/components/create-note-form.tsx:45
88+
msgid "Add a note"
89+
msgstr "Add a note"
90+
8791
#: app/routes/contact/detail/group.tsx:202
8892
msgid "Add Group"
8993
msgstr "Add Group"
@@ -93,10 +97,18 @@ msgstr "Add Group"
9397
msgid "Add Member"
9498
msgstr "Add Member"
9599

100+
#: app/features/domain/components/create-note-form.tsx:64
101+
msgid "Add Note"
102+
msgstr "Add Note"
103+
96104
#: app/routes/contact-group/detail/member.tsx:97
97105
msgid "Added"
98106
msgstr "Added"
99107

108+
#: app/features/domain/components/note-card.tsx:38
109+
msgid "Added by"
110+
msgstr "Added by"
111+
100112
#: app/features/activity/components/activity-list.tsx:407
101113
msgid "All read operations"
102114
msgstr "All read operations"
@@ -318,7 +330,7 @@ msgstr "Create"
318330
#: app/routes/project/detail/export-policy/index.tsx:58
319331
#: app/routes/project/detail/export-policy/detail.tsx:97
320332
#: app/routes/project/detail/domain/index.tsx:70
321-
#: app/routes/project/detail/domain/detail.tsx:129
333+
#: app/routes/project/detail/domain/detail.tsx:167
322334
#: app/routes/project/detail/dns/index.tsx:77
323335
#: app/routes/profile/session.tsx:56
324336
#: app/routes/organization/index.tsx:49
@@ -497,7 +509,7 @@ msgid "DNS Host"
497509
msgstr "DNS Host"
498510

499511
#: app/routes/project/detail/domain/index.tsx:52
500-
#: app/routes/project/detail/domain/detail.tsx:95
512+
#: app/routes/project/detail/domain/detail.tsx:133
501513
msgid "DNS Providers"
502514
msgstr "DNS Providers"
503515

@@ -510,7 +522,7 @@ msgid "DNS Records"
510522
msgstr "DNS Records"
511523

512524
#: app/routes/project/detail/domain/index.tsx:35
513-
#: app/routes/project/detail/domain/detail.tsx:75
525+
#: app/routes/project/detail/domain/detail.tsx:113
514526
msgid "Domain"
515527
msgstr "Domain"
516528

@@ -565,6 +577,10 @@ msgstr "End Time"
565577
msgid "Endpoint"
566578
msgstr "Endpoint"
567579

580+
#: app/features/domain/components/create-note-form.tsx:52
581+
msgid "Enter note content..."
582+
msgstr "Enter note content..."
583+
568584
#: app/routes/user/detail/index.tsx:136
569585
msgid "Enter reason for deactivation..."
570586
msgstr "Enter reason for deactivation..."
@@ -584,7 +600,7 @@ msgid "Enter the word <0>{confirmationText}</0> to perform this action."
584600
msgstr "Enter the word <0>{confirmationText}</0> to perform this action."
585601

586602
#: app/routes/project/detail/domain/index.tsx:56
587-
#: app/routes/project/detail/domain/detail.tsx:105
603+
#: app/routes/project/detail/domain/detail.tsx:143
588604
msgid "Expiration Date"
589605
msgstr "Expiration Date"
590606

@@ -918,7 +934,7 @@ msgstr "Nameservers"
918934

919935
#: app/routes/project/detail/secret.tsx:25
920936
#: app/routes/project/detail/export-policy/detail.tsx:75
921-
#: app/routes/project/detail/domain/detail.tsx:65
937+
#: app/routes/project/detail/domain/detail.tsx:103
922938
msgid "Namespace"
923939
msgstr "Namespace"
924940

@@ -930,6 +946,10 @@ msgstr "Navigation"
930946
msgid "New Limit"
931947
msgstr "New Limit"
932948

949+
#: app/features/domain/components/notes-list.tsx:30
950+
msgid "No notes yet. Add the first note below."
951+
msgstr "No notes yet. Add the first note below."
952+
933953
#: app/components/app-search/index.tsx:184
934954
msgid "No results found."
935955
msgstr "No results found."
@@ -943,6 +963,22 @@ msgstr "No users yet"
943963
msgid "Normal"
944964
msgstr "Normal"
945965

966+
#: app/features/domain/components/note-card.tsx:44
967+
msgid "Note"
968+
msgstr "Note"
969+
970+
#: app/features/domain/components/create-note-form.tsx:33
971+
msgid "Note added"
972+
msgstr "Note added"
973+
974+
#: app/features/domain/components/note-card.tsx:26
975+
msgid "Note deleted"
976+
msgstr "Note deleted"
977+
978+
#: app/routes/project/detail/domain/detail.tsx:184
979+
msgid "Notes"
980+
msgstr "Notes"
981+
946982
#: app/components/app-sidebar/nav-user.tsx:89
947983
msgid "Notifications"
948984
msgstr "Notifications"
@@ -1139,7 +1175,7 @@ msgid "Records"
11391175
msgstr "Records"
11401176

11411177
#: app/routes/project/detail/domain/index.tsx:48
1142-
#: app/routes/project/detail/domain/detail.tsx:85
1178+
#: app/routes/project/detail/domain/detail.tsx:123
11431179
#: app/routes/project/detail/dns/detail.tsx:195
11441180
msgid "Registrar"
11451181
msgstr "Registrar"
@@ -1170,7 +1206,7 @@ msgstr "Rejected"
11701206
msgid "Resend"
11711207
msgstr "Resend"
11721208

1173-
#: app/routes/project/detail/domain/detail.tsx:53
1209+
#: app/routes/project/detail/domain/detail.tsx:91
11741210
msgid "Resource Name"
11751211
msgstr "Resource Name"
11761212

@@ -1321,7 +1357,7 @@ msgstr "Start Time"
13211357
#: app/routes/project/detail/export-policy/detail.tsx:85
13221358
#: app/routes/project/detail/export-policy/detail.tsx:186
13231359
#: app/routes/project/detail/domain/index.tsx:60
1324-
#: app/routes/project/detail/domain/detail.tsx:115
1360+
#: app/routes/project/detail/domain/detail.tsx:153
13251361
#: app/routes/contact-group/index.tsx:60
13261362
#: app/routes/contact-group/detail/member.tsx:86
13271363
#: app/routes/contact/detail/group.tsx:78
@@ -1367,6 +1403,10 @@ msgstr "Temporarily prevent user from signing in. The user can be reactivated at
13671403
msgid "These items are checked every few minutes. If you've already made changes, they should resolve shortly."
13681404
msgstr "These items are checked every few minutes. If you've already made changes, they should resolve shortly."
13691405

1406+
#: app/features/domain/components/note-card.tsx:45
1407+
msgid "This note will be permanently deleted and cannot be recovered."
1408+
msgstr "This note will be permanently deleted and cannot be recovered."
1409+
13701410
#: app/features/activity/components/activity-list.tsx:424
13711411
msgid "Time range"
13721412
msgstr "Time range"
@@ -1392,6 +1432,10 @@ msgstr "Type"
13921432
msgid "Type a command or search..."
13931433
msgstr "Type a command or search..."
13941434

1435+
#: app/features/domain/components/note-card.tsx:38
1436+
msgid "Unknown"
1437+
msgstr "Unknown"
1438+
13951439
#: app/features/quota/components/quota-bucket-list.tsx:157
13961440
#: app/features/contact-group/components/contact-group-form.tsx:103
13971441
#: app/features/contact/components/contact-form.tsx:191

0 commit comments

Comments
 (0)