@joewinke/jatui 0.1.24 → 0.1.26
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/dist/actions/collapse.d.ts +80 -0
- package/dist/actions/collapse.js +134 -0
- package/dist/actions/railNav.d.ts +167 -0
- package/dist/actions/railNav.js +351 -0
- package/dist/components/AnnotationLayer.svelte +108 -0
- package/dist/components/AnnotationLayer.svelte.d.ts +12 -0
- package/dist/components/AnnotationPanel.svelte +319 -0
- package/dist/components/AnnotationPanel.svelte.d.ts +13 -0
- package/dist/components/AudioWaveform.svelte +701 -0
- package/dist/components/AudioWaveform.svelte.d.ts +19 -0
- package/dist/components/AvailabilityModal.svelte +177 -0
- package/dist/components/AvailabilityModal.svelte.d.ts +14 -0
- package/dist/components/AvatarUpload.svelte +234 -0
- package/dist/components/AvatarUpload.svelte.d.ts +23 -0
- package/dist/components/Badge.svelte +38 -0
- package/dist/components/Badge.svelte.d.ts +14 -0
- package/dist/components/BookingForm.svelte +278 -0
- package/dist/components/BookingForm.svelte.d.ts +37 -0
- package/dist/components/BurndownChart.svelte +778 -0
- package/dist/components/BurndownChart.svelte.d.ts +17 -0
- package/dist/components/Button.svelte +81 -0
- package/dist/components/Button.svelte.d.ts +21 -0
- package/dist/components/CalendarPicker.svelte +284 -0
- package/dist/components/CalendarPicker.svelte.d.ts +22 -0
- package/dist/components/Card.svelte +67 -0
- package/dist/components/Card.svelte.d.ts +17 -0
- package/dist/components/CharacterCounter.svelte +82 -0
- package/dist/components/CharacterCounter.svelte.d.ts +10 -0
- package/dist/components/ChipInput.svelte +609 -0
- package/dist/components/ChipInput.svelte.d.ts +55 -0
- package/dist/components/Collapse.svelte +91 -0
- package/dist/components/Collapse.svelte.d.ts +18 -0
- package/dist/components/ColorSelector.svelte +167 -0
- package/dist/components/ColorSelector.svelte.d.ts +9 -0
- package/dist/components/CommentThread.svelte +773 -0
- package/dist/components/CommentThread.svelte.d.ts +32 -0
- package/dist/components/ConfirmDialog.svelte +348 -0
- package/dist/components/ConfirmDialog.svelte.d.ts +3 -0
- package/dist/components/ConfirmModal.svelte +142 -0
- package/dist/components/ConfirmModal.svelte.d.ts +15 -0
- package/dist/components/ContextMenu.svelte +188 -0
- package/dist/components/ContextMenu.svelte.d.ts +31 -0
- package/dist/components/CountdownTimer.svelte +94 -0
- package/dist/components/CountdownTimer.svelte.d.ts +10 -0
- package/dist/components/DateRangePicker.svelte +194 -0
- package/dist/components/DateRangePicker.svelte.d.ts +10 -0
- package/dist/components/Drawer.svelte +143 -0
- package/dist/components/Drawer.svelte.d.ts +33 -0
- package/dist/components/EmojiPicker.svelte +159 -0
- package/dist/components/EmojiPicker.svelte.d.ts +8 -0
- package/dist/components/EntityPreviewCard.svelte +104 -0
- package/dist/components/EntityPreviewCard.svelte.d.ts +9 -0
- package/dist/components/FileDropzone.svelte +493 -0
- package/dist/components/FileDropzone.svelte.d.ts +40 -0
- package/dist/components/FilePicker.svelte +488 -0
- package/dist/components/FilePicker.svelte.d.ts +20 -0
- package/dist/components/FileThumbnail.svelte +80 -0
- package/dist/components/FileThumbnail.svelte.d.ts +12 -0
- package/dist/components/FilterDropdown.svelte +203 -0
- package/dist/components/FilterDropdown.svelte.d.ts +48 -0
- package/dist/components/FloatingActionBar.svelte +354 -0
- package/dist/components/FloatingActionBar.svelte.d.ts +51 -0
- package/dist/components/GPSTracker.svelte +202 -0
- package/dist/components/GPSTracker.svelte.d.ts +34 -0
- package/dist/components/HunkDiffView.svelte +348 -0
- package/dist/components/HunkDiffView.svelte.d.ts +9 -0
- package/dist/components/ImageLightbox.svelte +274 -0
- package/dist/components/ImageLightbox.svelte.d.ts +25 -0
- package/dist/components/ImageUpload.svelte +147 -0
- package/dist/components/ImageUpload.svelte.d.ts +10 -0
- package/dist/components/InlineEdit.svelte +291 -0
- package/dist/components/InlineEdit.svelte.d.ts +45 -0
- package/dist/components/InputDialog.svelte +327 -0
- package/dist/components/InputDialog.svelte.d.ts +3 -0
- package/dist/components/KeyboardShortcutsOverlay.svelte +296 -0
- package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +42 -0
- package/dist/components/LazyImage.svelte +123 -0
- package/dist/components/LazyImage.svelte.d.ts +14 -0
- package/dist/components/LinkShortener.svelte +274 -0
- package/dist/components/LinkShortener.svelte.d.ts +27 -0
- package/dist/components/LoadingSpinner.svelte +106 -0
- package/dist/components/LoadingSpinner.svelte.d.ts +21 -0
- package/dist/components/LocationMap.svelte +186 -0
- package/dist/components/LocationMap.svelte.d.ts +11 -0
- package/dist/components/MapView.svelte +341 -0
- package/dist/components/MapView.svelte.d.ts +20 -0
- package/dist/components/MarkupEditor.svelte +485 -0
- package/dist/components/MarkupEditor.svelte.d.ts +10 -0
- package/dist/components/MarkupOverlay.svelte +55 -0
- package/dist/components/MarkupOverlay.svelte.d.ts +8 -0
- package/dist/components/MediaWorkbench.svelte +871 -0
- package/dist/components/MediaWorkbench.svelte.d.ts +9 -0
- package/dist/components/MilestoneCard.svelte +98 -0
- package/dist/components/MilestoneCard.svelte.d.ts +16 -0
- package/dist/components/MilestoneTimeline.svelte +91 -0
- package/dist/components/MilestoneTimeline.svelte.d.ts +15 -0
- package/dist/components/Modal.svelte +244 -0
- package/dist/components/Modal.svelte.d.ts +35 -0
- package/dist/components/PDFViewer.svelte +105 -0
- package/dist/components/PDFViewer.svelte.d.ts +9 -0
- package/dist/components/PdfThumbnail.svelte +63 -0
- package/dist/components/PdfThumbnail.svelte.d.ts +9 -0
- package/dist/components/PhoneInput.svelte +212 -0
- package/dist/components/PhoneInput.svelte.d.ts +25 -0
- package/dist/components/ResizableDivider.svelte +307 -0
- package/dist/components/ResizableDivider.svelte.d.ts +26 -0
- package/dist/components/ResizablePanel.svelte +305 -0
- package/dist/components/ResizablePanel.svelte.d.ts +18 -0
- package/dist/components/SearchDropdown.svelte +421 -0
- package/dist/components/SearchDropdown.svelte.d.ts +24 -0
- package/dist/components/SelectInput.svelte +237 -0
- package/dist/components/SelectInput.svelte.d.ts +26 -0
- package/dist/components/SidebarUserFooter.svelte +34 -0
- package/dist/components/SidebarUserFooter.svelte.d.ts +16 -0
- package/dist/components/SignaturePad.svelte +175 -0
- package/dist/components/SignaturePad.svelte.d.ts +17 -0
- package/dist/components/SmartImageEditor.svelte +720 -0
- package/dist/components/SmartImageEditor.svelte.d.ts +24 -0
- package/dist/components/SortDropdown.svelte +156 -0
- package/dist/components/SortDropdown.svelte.d.ts +40 -0
- package/dist/components/Sparkline.svelte +116 -0
- package/dist/components/Sparkline.svelte.d.ts +12 -0
- package/dist/components/SpeechForm.svelte +114 -0
- package/dist/components/SpeechForm.svelte.d.ts +17 -0
- package/dist/components/StatusBadge.svelte +157 -0
- package/dist/components/StatusBadge.svelte.d.ts +24 -0
- package/dist/components/TaskTypeIcon.svelte +54 -0
- package/dist/components/TaskTypeIcon.svelte.d.ts +17 -0
- package/dist/components/TextArea.svelte +162 -0
- package/dist/components/TextArea.svelte.d.ts +30 -0
- package/dist/components/TextInput.svelte +131 -0
- package/dist/components/TextInput.svelte.d.ts +29 -0
- package/dist/components/ThemeSelector.svelte +206 -0
- package/dist/components/ThemeSelector.svelte.d.ts +14 -0
- package/dist/components/TimeSlotPicker.svelte +162 -0
- package/dist/components/TimeSlotPicker.svelte.d.ts +18 -0
- package/dist/components/UserAvatar.svelte +72 -0
- package/dist/components/UserAvatar.svelte.d.ts +11 -0
- package/dist/components/VariablePicker.svelte +170 -0
- package/dist/components/VariablePicker.svelte.d.ts +40 -0
- package/dist/components/VoicePlayer.svelte +422 -0
- package/dist/components/VoicePlayer.svelte.d.ts +18 -0
- package/dist/components/linked-columns/LinkedColumns.svelte +526 -0
- package/dist/components/linked-columns/LinkedColumns.svelte.d.ts +130 -0
- package/dist/components/markup.d.ts +47 -0
- package/dist/components/markup.js +201 -0
- package/dist/components/messaging/Avatar.svelte +81 -0
- package/dist/components/messaging/Avatar.svelte.d.ts +11 -0
- package/dist/components/messaging/ChannelInfoModal.svelte +163 -0
- package/dist/components/messaging/ChannelInfoModal.svelte.d.ts +18 -0
- package/dist/components/messaging/ChannelList.svelte +107 -0
- package/dist/components/messaging/ChannelList.svelte.d.ts +11 -0
- package/dist/components/messaging/ChannelMemberAvatarStack.svelte +69 -0
- package/dist/components/messaging/ChannelMemberAvatarStack.svelte.d.ts +11 -0
- package/dist/components/messaging/ChannelMembersModal.svelte +182 -0
- package/dist/components/messaging/ChannelMembersModal.svelte.d.ts +16 -0
- package/dist/components/messaging/CreateChannelModal.svelte +190 -0
- package/dist/components/messaging/CreateChannelModal.svelte.d.ts +14 -0
- package/dist/components/messaging/DirectMessageList.svelte +145 -0
- package/dist/components/messaging/DirectMessageList.svelte.d.ts +12 -0
- package/dist/components/messaging/EmojiSelector.svelte +261 -0
- package/dist/components/messaging/EmojiSelector.svelte.d.ts +19 -0
- package/dist/components/messaging/MentionAutocomplete.svelte +193 -0
- package/dist/components/messaging/MentionAutocomplete.svelte.d.ts +17 -0
- package/dist/components/messaging/MessageAttachment.svelte +273 -0
- package/dist/components/messaging/MessageAttachment.svelte.d.ts +13 -0
- package/dist/components/messaging/MessageAttachmentUpload.svelte +243 -0
- package/dist/components/messaging/MessageAttachmentUpload.svelte.d.ts +19 -0
- package/dist/components/messaging/MessageInput.svelte +452 -0
- package/dist/components/messaging/MessageInput.svelte.d.ts +28 -0
- package/dist/components/messaging/MessageItem.svelte +341 -0
- package/dist/components/messaging/MessageItem.svelte.d.ts +41 -0
- package/dist/components/messaging/MessageThread.svelte +306 -0
- package/dist/components/messaging/MessageThread.svelte.d.ts +30 -0
- package/dist/components/messaging/NotificationSettingsModal.svelte +234 -0
- package/dist/components/messaging/NotificationSettingsModal.svelte.d.ts +13 -0
- package/dist/components/messaging/QuotedMessageDisplay.svelte +123 -0
- package/dist/components/messaging/QuotedMessageDisplay.svelte.d.ts +10 -0
- package/dist/components/messaging/StartDMModal.svelte +100 -0
- package/dist/components/messaging/StartDMModal.svelte.d.ts +14 -0
- package/dist/components/messaging/ThreadPanel.svelte +153 -0
- package/dist/components/messaging/ThreadPanel.svelte.d.ts +16 -0
- package/dist/components/pipeline/DESIGN.md +361 -0
- package/dist/components/pipeline/Pipeline.svelte +391 -0
- package/dist/components/pipeline/Pipeline.svelte.d.ts +28 -0
- package/dist/components/pipeline/PipelineCard.svelte +94 -0
- package/dist/components/pipeline/PipelineCard.svelte.d.ts +13 -0
- package/dist/components/pipeline/PipelineColumn.svelte +163 -0
- package/dist/components/pipeline/PipelineColumn.svelte.d.ts +30 -0
- package/dist/components/replay/ChapterTimeline.svelte +326 -0
- package/dist/components/replay/ChapterTimeline.svelte.d.ts +39 -0
- package/dist/components/session-nav/transcriptModel.d.ts +132 -0
- package/dist/components/session-nav/transcriptModel.js +285 -0
- package/dist/data/emojis.d.ts +18 -0
- package/dist/data/emojis.js +1196 -0
- package/dist/index.d.ts +142 -0
- package/dist/index.js +142 -0
- package/dist/stores/confirmDialog.svelte.d.ts +21 -0
- package/dist/stores/confirmDialog.svelte.js +31 -0
- package/dist/stores/inputDialog.svelte.d.ts +24 -0
- package/dist/stores/inputDialog.svelte.js +32 -0
- package/dist/styles/rail.css +63 -0
- package/dist/types/annotation.d.ts +32 -0
- package/dist/types/annotation.js +3 -0
- package/dist/types/booking.d.ts +115 -0
- package/dist/types/booking.js +24 -0
- package/dist/types/comments.d.ts +65 -0
- package/dist/types/comments.js +22 -0
- package/dist/types/entityPreview.d.ts +42 -0
- package/dist/types/entityPreview.js +10 -0
- package/dist/types/filePicker.d.ts +15 -0
- package/dist/types/filePicker.js +1 -0
- package/dist/types/googleMaps.d.ts +51 -0
- package/dist/types/maps.d.ts +43 -0
- package/dist/types/maps.js +4 -0
- package/dist/types/messaging.d.ts +282 -0
- package/dist/types/messaging.js +100 -0
- package/dist/types/milestone.d.ts +20 -0
- package/dist/types/milestone.js +7 -0
- package/dist/types/pipeline.d.ts +115 -0
- package/dist/types/pipeline.js +15 -0
- package/dist/types/smartImageEditor.d.ts +42 -0
- package/dist/types/smartImageEditor.js +1 -0
- package/dist/types/templateVars.d.ts +34 -0
- package/dist/types/templateVars.js +12 -0
- package/dist/utils/currency.d.ts +8 -0
- package/dist/utils/currency.js +17 -0
- package/dist/utils/daisyuiColors.d.ts +37 -0
- package/dist/utils/daisyuiColors.js +152 -0
- package/dist/utils/dateFormatters.d.ts +69 -0
- package/dist/utils/dateFormatters.js +199 -0
- package/dist/utils/googleMapsLoader.d.ts +19 -0
- package/dist/utils/googleMapsLoader.js +121 -0
- package/dist/utils/mentionParser.d.ts +67 -0
- package/dist/utils/mentionParser.js +132 -0
- package/dist/utils/phone.d.ts +13 -0
- package/dist/utils/phone.js +67 -0
- package/dist/utils/phoneFormat.d.ts +26 -0
- package/dist/utils/phoneFormat.js +69 -0
- package/dist/utils/taskUtils.d.ts +37 -0
- package/dist/utils/taskUtils.js +71 -0
- package/package.json +3 -2
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Entity kinds a comment thread can hang off — mirrors the host DB CHECK. */
|
|
2
|
+
export type CommentEntityType = 'project' | 'invoice' | 'contract' | 'asset' | 'task' | 'form_submission' | 'milestone' | 'contract_term';
|
|
3
|
+
export declare const COMMENT_ENTITY_TYPES: readonly CommentEntityType[];
|
|
4
|
+
/** Internal = staff-only. Client = also visible in the customer portal. */
|
|
5
|
+
export type CommentVisibility = 'internal' | 'client';
|
|
6
|
+
export declare const COMMENT_VISIBILITIES: readonly CommentVisibility[];
|
|
7
|
+
export interface Comment {
|
|
8
|
+
id: string;
|
|
9
|
+
team_id: string;
|
|
10
|
+
entity_type: CommentEntityType;
|
|
11
|
+
entity_id: string;
|
|
12
|
+
author_id: string;
|
|
13
|
+
/** Joined author display name (null if the profile is gone). */
|
|
14
|
+
author_name: string | null;
|
|
15
|
+
/** Joined author avatar URL. */
|
|
16
|
+
author_avatar_url: string | null;
|
|
17
|
+
body: string;
|
|
18
|
+
/** null = top-level; otherwise the id of the top-level comment it replies to. */
|
|
19
|
+
parent_id: string | null;
|
|
20
|
+
visibility: CommentVisibility;
|
|
21
|
+
/** Set the first time the body is edited. */
|
|
22
|
+
edited_at: string | null;
|
|
23
|
+
/** Set on soft-delete; body is replaced with "[deleted]". */
|
|
24
|
+
deleted_at: string | null;
|
|
25
|
+
created_at: string;
|
|
26
|
+
/** Number of replies (top-level rows only; always 0 for replies). */
|
|
27
|
+
reply_count: number;
|
|
28
|
+
/**
|
|
29
|
+
* Replies are loaded on demand; this is [] in the paginated top-level
|
|
30
|
+
* listing and populated by the component when a thread is expanded.
|
|
31
|
+
*/
|
|
32
|
+
replies: Comment[];
|
|
33
|
+
}
|
|
34
|
+
export interface CommentCreateInput {
|
|
35
|
+
entityType: CommentEntityType;
|
|
36
|
+
entityId: string;
|
|
37
|
+
body: string;
|
|
38
|
+
/** When set, this comment is a reply to the given TOP-LEVEL comment. */
|
|
39
|
+
parentId?: string | null;
|
|
40
|
+
/** Defaults to "internal". Clamped to the parent's visibility for replies. */
|
|
41
|
+
visibility?: CommentVisibility;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Transport-agnostic data layer for `<CommentThread>`. The consuming app
|
|
45
|
+
* wires these to a server load / form action / fetch endpoint. The
|
|
46
|
+
* component never imports server code or knows how data is fetched.
|
|
47
|
+
*/
|
|
48
|
+
export interface CommentCallbacks {
|
|
49
|
+
/** Persist a new comment (top-level or reply) and return the created row. */
|
|
50
|
+
submitComment: (input: CommentCreateInput) => Promise<Comment>;
|
|
51
|
+
/** Edit an existing comment body; returns the updated row. */
|
|
52
|
+
editComment: (id: string, body: string) => Promise<Comment>;
|
|
53
|
+
/** Soft-delete a comment. The row stays so replies remain anchored. */
|
|
54
|
+
deleteComment: (id: string) => Promise<void>;
|
|
55
|
+
/** Fetch the replies for a top-level comment (oldest-first). */
|
|
56
|
+
loadReplies: (parentId: string) => Promise<Comment[]>;
|
|
57
|
+
/**
|
|
58
|
+
* Optional: fetch the next page of top-level comments. When omitted, the
|
|
59
|
+
* "Load more" affordance is hidden.
|
|
60
|
+
*/
|
|
61
|
+
loadMore?: (offset: number) => Promise<{
|
|
62
|
+
comments: Comment[];
|
|
63
|
+
total: number;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Threaded Comments Types
|
|
2
|
+
// Generic types for the CommentThread UI component — no framework or backend
|
|
3
|
+
// dependencies. Mirrors the JST `$lib/types/comments` projection so the
|
|
4
|
+
// component is a drop-in for any app whose server speaks the same shape.
|
|
5
|
+
//
|
|
6
|
+
// Threading is ONE level deep on the data side: a comment is either
|
|
7
|
+
// top-level (`parent_id === null`) or a single-level reply. The UI renders
|
|
8
|
+
// this as two visual levels (a comment and its replies).
|
|
9
|
+
export var COMMENT_ENTITY_TYPES = [
|
|
10
|
+
'project',
|
|
11
|
+
'invoice',
|
|
12
|
+
'contract',
|
|
13
|
+
'asset',
|
|
14
|
+
'task',
|
|
15
|
+
'form_submission',
|
|
16
|
+
'milestone',
|
|
17
|
+
'contract_term'
|
|
18
|
+
];
|
|
19
|
+
export var COMMENT_VISIBILITIES = [
|
|
20
|
+
'internal',
|
|
21
|
+
'client'
|
|
22
|
+
];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity preview card — the uniform shape an EntityPreviewCard renders and that
|
|
3
|
+
* a server-side resolver returns for any domain entity. One shape for every
|
|
4
|
+
* entity type so the card and hover-action stay generic and the same card backs
|
|
5
|
+
* hover-previews, @-mention unfurls, and link previews.
|
|
6
|
+
*
|
|
7
|
+
* Pure types — safe to import from both server (the resolver) and client (the
|
|
8
|
+
* card + action).
|
|
9
|
+
*/
|
|
10
|
+
/** A small labelled stat shown in the card's footer grid (e.g. "Submissions" → 42). */
|
|
11
|
+
export interface EntityPreviewStat {
|
|
12
|
+
label: string;
|
|
13
|
+
value: string | number;
|
|
14
|
+
}
|
|
15
|
+
/** A coloured pill (status, type, …). `tone` maps to a DaisyUI semantic token. */
|
|
16
|
+
export interface EntityPreviewBadge {
|
|
17
|
+
label: string;
|
|
18
|
+
tone?: "neutral" | "primary" | "info" | "success" | "warning" | "error";
|
|
19
|
+
}
|
|
20
|
+
export interface EntityPreviewData {
|
|
21
|
+
/** Canonical entity type slug (e.g. "base", "vendor", "form", "task"). */
|
|
22
|
+
type: string;
|
|
23
|
+
id: string;
|
|
24
|
+
/** Human label for the type, shown as the card's kicker (e.g. "Knowledge base"). */
|
|
25
|
+
typeLabel: string;
|
|
26
|
+
/** Main title. */
|
|
27
|
+
title: string;
|
|
28
|
+
/** Optional one-line subtitle under the title (e.g. a vendor's company). */
|
|
29
|
+
subtitle?: string | null;
|
|
30
|
+
/** Optional longer summary / description (clamped in the card). */
|
|
31
|
+
summary?: string | null;
|
|
32
|
+
/** og:image / avatar / thumbnail URL, when the entity has one. */
|
|
33
|
+
imageUrl?: string | null;
|
|
34
|
+
/** A short emoji/text glyph fallback when there's no image. */
|
|
35
|
+
icon?: string | null;
|
|
36
|
+
/** Up to ~4 labelled stats. */
|
|
37
|
+
stats?: EntityPreviewStat[];
|
|
38
|
+
/** Up to ~3 status/type pills. */
|
|
39
|
+
badges?: EntityPreviewBadge[];
|
|
40
|
+
/** The admin detail route for the entity (the card links here). */
|
|
41
|
+
href?: string | null;
|
|
42
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity preview card — the uniform shape an EntityPreviewCard renders and that
|
|
3
|
+
* a server-side resolver returns for any domain entity. One shape for every
|
|
4
|
+
* entity type so the card and hover-action stay generic and the same card backs
|
|
5
|
+
* hover-previews, @-mention unfurls, and link previews.
|
|
6
|
+
*
|
|
7
|
+
* Pure types — safe to import from both server (the resolver) and client (the
|
|
8
|
+
* card + action).
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface FilePickerFile {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
created_at: string;
|
|
5
|
+
updated_at: string;
|
|
6
|
+
metadata: Record<string, any>;
|
|
7
|
+
scope: "personal" | "team";
|
|
8
|
+
/** Publicly accessible URL (no auth required). When present, used instead of the /api/files proxy URL. */
|
|
9
|
+
publicUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FilePickerSelection {
|
|
12
|
+
file: FilePickerFile;
|
|
13
|
+
url: string;
|
|
14
|
+
downloadUrl: string;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ambient declarations for Google Maps JS API loaded at runtime.
|
|
3
|
+
* Prevents "Property 'google' does not exist on Window" TS errors.
|
|
4
|
+
* Components using the Google Maps API operate with `any` for advanced
|
|
5
|
+
* marker types until @types/google.maps is added as a dev dependency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare namespace google {
|
|
9
|
+
namespace maps {
|
|
10
|
+
class Map {
|
|
11
|
+
constructor(el: HTMLElement, options: object);
|
|
12
|
+
fitBounds(bounds: any): void;
|
|
13
|
+
getZoom(): number | undefined;
|
|
14
|
+
setZoom(zoom: number): void;
|
|
15
|
+
setCenter(center: object): void;
|
|
16
|
+
}
|
|
17
|
+
class InfoWindow {
|
|
18
|
+
constructor(options: object);
|
|
19
|
+
open(map: any, marker: any): void;
|
|
20
|
+
}
|
|
21
|
+
class Circle {
|
|
22
|
+
constructor(options: object);
|
|
23
|
+
setMap(map: any): void;
|
|
24
|
+
}
|
|
25
|
+
class LatLngBounds {
|
|
26
|
+
extend(position: object): void;
|
|
27
|
+
}
|
|
28
|
+
const MapTypeId: { ROADMAP: string };
|
|
29
|
+
const event: {
|
|
30
|
+
addListener(instance: any, event: string, fn: () => void): any;
|
|
31
|
+
removeListener(listener: any): void;
|
|
32
|
+
clearInstanceListeners(instance: any): void;
|
|
33
|
+
};
|
|
34
|
+
namespace marker {
|
|
35
|
+
class AdvancedMarkerElement {
|
|
36
|
+
constructor(options: object);
|
|
37
|
+
map: any;
|
|
38
|
+
position: any;
|
|
39
|
+
addListener(event: string, fn: () => void): void;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
const places: any;
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const geometry: any;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface Window {
|
|
50
|
+
google?: typeof google;
|
|
51
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for MapView, LocationMap, and GPSTracker components.
|
|
3
|
+
*/
|
|
4
|
+
export interface MapJob {
|
|
5
|
+
id: string;
|
|
6
|
+
status: string;
|
|
7
|
+
type?: string;
|
|
8
|
+
priority?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
scheduled_at?: string;
|
|
11
|
+
customer?: {
|
|
12
|
+
name?: string;
|
|
13
|
+
phone?: string;
|
|
14
|
+
};
|
|
15
|
+
location?: {
|
|
16
|
+
address?: string;
|
|
17
|
+
latitude?: number;
|
|
18
|
+
longitude?: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface MapAgent {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
email?: string;
|
|
25
|
+
avatarUrl?: string;
|
|
26
|
+
status?: string;
|
|
27
|
+
capacity?: string;
|
|
28
|
+
specializations?: string[];
|
|
29
|
+
vehicle?: {
|
|
30
|
+
name?: string;
|
|
31
|
+
};
|
|
32
|
+
/** Most recent GPS fix */
|
|
33
|
+
location?: {
|
|
34
|
+
latitude?: number;
|
|
35
|
+
longitude?: number;
|
|
36
|
+
age_seconds?: number;
|
|
37
|
+
};
|
|
38
|
+
/** Active dispatches used as fallback location */
|
|
39
|
+
dispatches?: Array<{
|
|
40
|
+
job_id: string;
|
|
41
|
+
status: string;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
export interface Message {
|
|
2
|
+
id: string;
|
|
3
|
+
content: string;
|
|
4
|
+
senderId: string;
|
|
5
|
+
senderName: string;
|
|
6
|
+
senderAvatar?: string;
|
|
7
|
+
createdAt: string;
|
|
8
|
+
editedAt?: string;
|
|
9
|
+
isRead?: boolean;
|
|
10
|
+
readByCount?: number;
|
|
11
|
+
isPinned?: boolean;
|
|
12
|
+
pinnedAt?: string;
|
|
13
|
+
pinnedBy?: string;
|
|
14
|
+
pinnedByName?: string;
|
|
15
|
+
parentMessageId?: string;
|
|
16
|
+
recipientId?: string;
|
|
17
|
+
thread_reply_count?: number;
|
|
18
|
+
thread_last_reply_at?: string;
|
|
19
|
+
thread_last_sender?: {
|
|
20
|
+
id: string;
|
|
21
|
+
full_name: string;
|
|
22
|
+
avatar_url?: string;
|
|
23
|
+
};
|
|
24
|
+
quotedMessageId?: string;
|
|
25
|
+
quotedMessage?: QuotedMessage;
|
|
26
|
+
attachments?: MessageAttachment[];
|
|
27
|
+
}
|
|
28
|
+
export interface QuotedMessage {
|
|
29
|
+
id: string;
|
|
30
|
+
content: string;
|
|
31
|
+
senderId: string;
|
|
32
|
+
senderName: string;
|
|
33
|
+
senderAvatar?: string;
|
|
34
|
+
createdAt: string;
|
|
35
|
+
}
|
|
36
|
+
export interface MessageAttachment {
|
|
37
|
+
id: string;
|
|
38
|
+
file_name: string;
|
|
39
|
+
file_type: string;
|
|
40
|
+
file_size: number;
|
|
41
|
+
storage_path: string;
|
|
42
|
+
width?: number;
|
|
43
|
+
height?: number;
|
|
44
|
+
duration?: number;
|
|
45
|
+
thumbnail_path?: string;
|
|
46
|
+
uploaded_by: string;
|
|
47
|
+
created_at: string;
|
|
48
|
+
}
|
|
49
|
+
export interface PendingAttachment {
|
|
50
|
+
id: string;
|
|
51
|
+
file: File;
|
|
52
|
+
preview?: string;
|
|
53
|
+
uploading: boolean;
|
|
54
|
+
progress: number;
|
|
55
|
+
error?: string;
|
|
56
|
+
storagePath?: string;
|
|
57
|
+
width?: number;
|
|
58
|
+
height?: number;
|
|
59
|
+
}
|
|
60
|
+
export interface Channel {
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
type: 'public' | 'private' | 'direct';
|
|
65
|
+
isActive: boolean;
|
|
66
|
+
memberRole: 'admin' | 'moderator' | 'member';
|
|
67
|
+
joinedAt: string;
|
|
68
|
+
createdAt: string;
|
|
69
|
+
unreadCount?: number;
|
|
70
|
+
emoji?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface ChannelMember {
|
|
73
|
+
id: string;
|
|
74
|
+
userId: string;
|
|
75
|
+
fullName: string;
|
|
76
|
+
avatarUrl?: string | null;
|
|
77
|
+
role: 'admin' | 'moderator' | 'member';
|
|
78
|
+
userRole?: string;
|
|
79
|
+
}
|
|
80
|
+
export interface DirectConversation {
|
|
81
|
+
id: string;
|
|
82
|
+
name: string;
|
|
83
|
+
avatar?: string;
|
|
84
|
+
lastMessageAt: string;
|
|
85
|
+
unreadCount?: number;
|
|
86
|
+
isOnline?: boolean;
|
|
87
|
+
}
|
|
88
|
+
export interface OrgMember {
|
|
89
|
+
id: string;
|
|
90
|
+
full_name: string;
|
|
91
|
+
avatar_url?: string;
|
|
92
|
+
role: string;
|
|
93
|
+
active?: boolean;
|
|
94
|
+
}
|
|
95
|
+
export type EmojiCategory = 'search' | 'frequently_used' | 'smileys_people' | 'animals_nature' | 'food_drink' | 'travel_places' | 'activities' | 'objects' | 'symbols' | 'flags' | 'custom';
|
|
96
|
+
export type SkinTone = 'default' | 'light' | 'medium-light' | 'medium' | 'medium-dark' | 'dark';
|
|
97
|
+
export interface StandardEmoji {
|
|
98
|
+
unicode: string;
|
|
99
|
+
name: string;
|
|
100
|
+
shortcode: string;
|
|
101
|
+
category: EmojiCategory;
|
|
102
|
+
keywords: string[];
|
|
103
|
+
skinTones?: string[];
|
|
104
|
+
}
|
|
105
|
+
export interface CustomEmoji {
|
|
106
|
+
id: string;
|
|
107
|
+
organizationId: string;
|
|
108
|
+
name: string;
|
|
109
|
+
displayName: string;
|
|
110
|
+
description?: string;
|
|
111
|
+
fileName: string;
|
|
112
|
+
fileSize: number;
|
|
113
|
+
contentType: 'image/png' | 'image/gif';
|
|
114
|
+
storagePath: string;
|
|
115
|
+
isAnimated: boolean;
|
|
116
|
+
isActive: boolean;
|
|
117
|
+
usageCount: number;
|
|
118
|
+
uploadedBy: string;
|
|
119
|
+
createdAt: string;
|
|
120
|
+
updatedAt: string;
|
|
121
|
+
}
|
|
122
|
+
export interface EmojiReaction {
|
|
123
|
+
id: string;
|
|
124
|
+
messageId: string;
|
|
125
|
+
userId: string;
|
|
126
|
+
emojiUnicode?: string;
|
|
127
|
+
customEmojiId?: string;
|
|
128
|
+
skinTone?: SkinTone;
|
|
129
|
+
createdAt: string;
|
|
130
|
+
}
|
|
131
|
+
export interface ReactionSummary {
|
|
132
|
+
emojiUnicode?: string;
|
|
133
|
+
customEmojiId?: string;
|
|
134
|
+
customEmojiName?: string;
|
|
135
|
+
customEmojiStoragePath?: string;
|
|
136
|
+
customEmojiIsAnimated?: boolean;
|
|
137
|
+
reactionCount: number;
|
|
138
|
+
userReacted: boolean;
|
|
139
|
+
recentUsers: Array<{
|
|
140
|
+
id: string;
|
|
141
|
+
name: string;
|
|
142
|
+
}>;
|
|
143
|
+
}
|
|
144
|
+
export interface EmojiSelection {
|
|
145
|
+
type: 'standard' | 'custom';
|
|
146
|
+
unicode?: string;
|
|
147
|
+
customEmojiId?: string;
|
|
148
|
+
name: string;
|
|
149
|
+
shortcode: string;
|
|
150
|
+
skinTone?: SkinTone;
|
|
151
|
+
}
|
|
152
|
+
export interface EmojiPreferences {
|
|
153
|
+
skinTone: SkinTone;
|
|
154
|
+
frequentlyUsed: string[];
|
|
155
|
+
lastUsedAt?: string;
|
|
156
|
+
customEmojiFavorites?: string[];
|
|
157
|
+
}
|
|
158
|
+
export interface EmojiCategoryInfo {
|
|
159
|
+
id: EmojiCategory;
|
|
160
|
+
name: string;
|
|
161
|
+
icon: string;
|
|
162
|
+
description: string;
|
|
163
|
+
}
|
|
164
|
+
export interface SkinToneInfo {
|
|
165
|
+
id: SkinTone;
|
|
166
|
+
name: string;
|
|
167
|
+
unicode: string;
|
|
168
|
+
preview: string;
|
|
169
|
+
}
|
|
170
|
+
export interface MentionItem {
|
|
171
|
+
type: string;
|
|
172
|
+
id: string;
|
|
173
|
+
name: string;
|
|
174
|
+
secondary: string;
|
|
175
|
+
url: string;
|
|
176
|
+
}
|
|
177
|
+
export interface NotificationChannels {
|
|
178
|
+
email: boolean;
|
|
179
|
+
sms: boolean;
|
|
180
|
+
}
|
|
181
|
+
export interface NotificationEvents {
|
|
182
|
+
[key: string]: boolean;
|
|
183
|
+
}
|
|
184
|
+
export interface NotificationTiming {
|
|
185
|
+
[key: string]: string | number;
|
|
186
|
+
}
|
|
187
|
+
export interface NotificationPreferences {
|
|
188
|
+
channels: NotificationChannels;
|
|
189
|
+
events: NotificationEvents;
|
|
190
|
+
timing: NotificationTiming;
|
|
191
|
+
}
|
|
192
|
+
export interface PresenceState {
|
|
193
|
+
[userId: string]: {
|
|
194
|
+
status: string;
|
|
195
|
+
technicianStatus?: string;
|
|
196
|
+
lastSeen?: string;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export interface MessageCallbacks {
|
|
200
|
+
loadMessages: (params: {
|
|
201
|
+
channelId?: string;
|
|
202
|
+
dmUserId?: string;
|
|
203
|
+
before?: string;
|
|
204
|
+
limit?: number;
|
|
205
|
+
}) => Promise<Message[]>;
|
|
206
|
+
sendMessage: (params: {
|
|
207
|
+
content: string;
|
|
208
|
+
channelId?: string;
|
|
209
|
+
dmUserId?: string;
|
|
210
|
+
parentMessageId?: string;
|
|
211
|
+
quotedMessageId?: string;
|
|
212
|
+
attachments?: PendingAttachment[];
|
|
213
|
+
priority?: string;
|
|
214
|
+
}) => Promise<Message>;
|
|
215
|
+
editMessage: (messageId: string, content: string) => Promise<Message>;
|
|
216
|
+
deleteMessage: (messageId: string) => Promise<void>;
|
|
217
|
+
pinMessage: (messageId: string, channelId: string) => Promise<void>;
|
|
218
|
+
unpinMessage: (messageId: string, channelId: string) => Promise<void>;
|
|
219
|
+
markAsRead: (messageIds: string[]) => Promise<void>;
|
|
220
|
+
}
|
|
221
|
+
export interface ReactionCallbacks {
|
|
222
|
+
loadReactions: (messageId: string) => Promise<ReactionSummary[]>;
|
|
223
|
+
addReaction: (messageId: string, emoji: {
|
|
224
|
+
unicode?: string;
|
|
225
|
+
customEmojiId?: string;
|
|
226
|
+
skinTone?: SkinTone;
|
|
227
|
+
}) => Promise<void>;
|
|
228
|
+
removeReaction: (messageId: string, emoji: {
|
|
229
|
+
unicode?: string;
|
|
230
|
+
customEmojiId?: string;
|
|
231
|
+
}) => Promise<void>;
|
|
232
|
+
}
|
|
233
|
+
export interface AttachmentCallbacks {
|
|
234
|
+
getSignedUrl: (storagePath: string) => Promise<string>;
|
|
235
|
+
getThumbnailUrl: (thumbnailPath: string) => Promise<string | null>;
|
|
236
|
+
uploadFile: (file: File, organizationId: string) => Promise<{
|
|
237
|
+
path: string;
|
|
238
|
+
error?: string;
|
|
239
|
+
}>;
|
|
240
|
+
deleteAttachment: (attachmentId: string) => Promise<void>;
|
|
241
|
+
}
|
|
242
|
+
export interface MentionCallbacks {
|
|
243
|
+
searchMentions: (query: string) => Promise<MentionItem[]>;
|
|
244
|
+
}
|
|
245
|
+
export interface EmojiCallbacks {
|
|
246
|
+
loadCustomEmojis: () => Promise<CustomEmoji[]>;
|
|
247
|
+
loadPreferences: () => Promise<EmojiPreferences>;
|
|
248
|
+
savePreferences: (prefs: Partial<EmojiPreferences>) => Promise<void>;
|
|
249
|
+
}
|
|
250
|
+
export interface ChannelCallbacks {
|
|
251
|
+
createChannel: (params: {
|
|
252
|
+
name: string;
|
|
253
|
+
description?: string;
|
|
254
|
+
type: 'public' | 'private';
|
|
255
|
+
emoji?: string;
|
|
256
|
+
memberIds?: string[];
|
|
257
|
+
}) => Promise<Channel>;
|
|
258
|
+
updateChannel: (channelId: string, params: Partial<Pick<Channel, 'name' | 'description' | 'emoji'>>) => Promise<Channel>;
|
|
259
|
+
deleteChannel: (channelId: string) => Promise<void>;
|
|
260
|
+
leaveChannel: (channelId: string) => Promise<void>;
|
|
261
|
+
getMembers: (channelId: string) => Promise<ChannelMember[]>;
|
|
262
|
+
addMember: (channelId: string, userId: string) => Promise<void>;
|
|
263
|
+
removeMember: (channelId: string, userId: string) => Promise<void>;
|
|
264
|
+
updateMemberRole: (channelId: string, userId: string, role: 'admin' | 'moderator' | 'member') => Promise<void>;
|
|
265
|
+
}
|
|
266
|
+
export interface ThreadCallbacks {
|
|
267
|
+
loadReplies: (parentMessageId: string) => Promise<Message[]>;
|
|
268
|
+
sendReply: (parentMessageId: string, content: string, attachments?: PendingAttachment[]) => Promise<Message>;
|
|
269
|
+
}
|
|
270
|
+
export interface NotificationCallbacks {
|
|
271
|
+
loadSettings: (params: {
|
|
272
|
+
type: 'global' | 'channel';
|
|
273
|
+
channelId?: string;
|
|
274
|
+
}) => Promise<NotificationPreferences>;
|
|
275
|
+
saveSettings: (params: {
|
|
276
|
+
type: 'global' | 'channel';
|
|
277
|
+
channelId?: string;
|
|
278
|
+
}, settings: NotificationPreferences) => Promise<void>;
|
|
279
|
+
}
|
|
280
|
+
export declare const EMOJI_CATEGORIES: EmojiCategoryInfo[];
|
|
281
|
+
export declare const SKIN_TONES: SkinToneInfo[];
|
|
282
|
+
export declare const DEFAULT_QUICK_REACTIONS: StandardEmoji[];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Messaging System Types
|
|
2
|
+
// Generic types for messaging UI components — no framework or backend dependencies
|
|
3
|
+
// ─── Emoji Data Constants ─────────────────────────────────────────────
|
|
4
|
+
export var EMOJI_CATEGORIES = [
|
|
5
|
+
{ id: 'search', name: 'Search', icon: '🔍', description: 'Search results' },
|
|
6
|
+
{
|
|
7
|
+
id: 'frequently_used',
|
|
8
|
+
name: 'Frequently Used',
|
|
9
|
+
icon: '🕐',
|
|
10
|
+
description: 'Your most used emojis'
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 'smileys_people',
|
|
14
|
+
name: 'Smileys & People',
|
|
15
|
+
icon: '😀',
|
|
16
|
+
description: 'Faces, people, and emotions'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'animals_nature',
|
|
20
|
+
name: 'Animals & Nature',
|
|
21
|
+
icon: '🐻',
|
|
22
|
+
description: 'Animals, plants, and nature'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'food_drink',
|
|
26
|
+
name: 'Food & Drink',
|
|
27
|
+
icon: '🍎',
|
|
28
|
+
description: 'Food, drinks, and meals'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'travel_places',
|
|
32
|
+
name: 'Travel & Places',
|
|
33
|
+
icon: '🏠',
|
|
34
|
+
description: 'Places, transport, and travel'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'activities',
|
|
38
|
+
name: 'Activities',
|
|
39
|
+
icon: '⚽',
|
|
40
|
+
description: 'Sports, games, and activities'
|
|
41
|
+
},
|
|
42
|
+
{ id: 'objects', name: 'Objects', icon: '🔨', description: 'Tools, objects, and items' },
|
|
43
|
+
{
|
|
44
|
+
id: 'symbols',
|
|
45
|
+
name: 'Symbols',
|
|
46
|
+
icon: '❤️',
|
|
47
|
+
description: 'Symbols, signs, and icons'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'flags',
|
|
51
|
+
name: 'Flags',
|
|
52
|
+
icon: '🏳️',
|
|
53
|
+
description: 'Country and regional flags'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'custom',
|
|
57
|
+
name: 'Custom',
|
|
58
|
+
icon: '🎨',
|
|
59
|
+
description: 'Organization custom emojis'
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
export var SKIN_TONES = [
|
|
63
|
+
{ id: 'default', name: 'Default', unicode: '', preview: '👋' },
|
|
64
|
+
{ id: 'light', name: 'Light', unicode: '🏻', preview: '👋🏻' },
|
|
65
|
+
{ id: 'medium-light', name: 'Medium Light', unicode: '🏼', preview: '👋🏼' },
|
|
66
|
+
{ id: 'medium', name: 'Medium', unicode: '🏽', preview: '👋🏽' },
|
|
67
|
+
{ id: 'medium-dark', name: 'Medium Dark', unicode: '🏾', preview: '👋🏾' },
|
|
68
|
+
{ id: 'dark', name: 'Dark', unicode: '🏿', preview: '👋🏿' }
|
|
69
|
+
];
|
|
70
|
+
// Default quick-reaction emojis (used when no user preferences available)
|
|
71
|
+
export var DEFAULT_QUICK_REACTIONS = [
|
|
72
|
+
{
|
|
73
|
+
unicode: '👍',
|
|
74
|
+
name: 'Thumbs Up',
|
|
75
|
+
shortcode: 'thumbsup',
|
|
76
|
+
category: 'smileys_people',
|
|
77
|
+
keywords: ['like', 'approve', 'yes']
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
unicode: '❤️',
|
|
81
|
+
name: 'Red Heart',
|
|
82
|
+
shortcode: 'heart',
|
|
83
|
+
category: 'symbols',
|
|
84
|
+
keywords: ['love', 'heart']
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
unicode: '😂',
|
|
88
|
+
name: 'Face with Tears of Joy',
|
|
89
|
+
shortcode: 'joy',
|
|
90
|
+
category: 'smileys_people',
|
|
91
|
+
keywords: ['laugh', 'lol', 'funny']
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
unicode: '🎉',
|
|
95
|
+
name: 'Party Popper',
|
|
96
|
+
shortcode: 'tada',
|
|
97
|
+
category: 'activities',
|
|
98
|
+
keywords: ['party', 'celebrate']
|
|
99
|
+
}
|
|
100
|
+
];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic milestone/timeline types for stepped workflows.
|
|
3
|
+
*
|
|
4
|
+
* Used by MilestoneCard and MilestoneTimeline components.
|
|
5
|
+
* Projects extend this with domain-specific fields (e.g. stripe_invoice_id).
|
|
6
|
+
*/
|
|
7
|
+
export type MilestoneStatus = "pending" | "delivered" | "accepted" | "paid";
|
|
8
|
+
export interface Milestone {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
percentage: number;
|
|
13
|
+
amount: number;
|
|
14
|
+
acceptance_criteria: string;
|
|
15
|
+
status: MilestoneStatus;
|
|
16
|
+
sort_order: number;
|
|
17
|
+
delivered_at: string | null;
|
|
18
|
+
accepted_at: string | null;
|
|
19
|
+
paid_at: string | null;
|
|
20
|
+
}
|