@stll/workspace-ui 0.12.6 → 0.13.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/README.md +139 -0
- package/dist/feature-previews/billing-preview.d.ts +38 -0
- package/dist/feature-previews/billing-preview.js +160 -0
- package/dist/feature-previews/contacts-preview.d.ts +35 -0
- package/dist/feature-previews/contacts-preview.js +109 -0
- package/dist/feature-previews/documents-preview.d.ts +44 -0
- package/dist/feature-previews/documents-preview.js +105 -0
- package/dist/feature-previews/format.d.ts +16 -0
- package/dist/feature-previews/format.js +20 -0
- package/dist/feature-previews/index.d.ts +9 -0
- package/dist/feature-previews/index.js +7 -0
- package/dist/feature-previews/matters-preview.d.ts +30 -0
- package/dist/feature-previews/matters-preview.js +153 -0
- package/dist/feature-previews/mock-data.d.ts +41 -0
- package/dist/feature-previews/mock-data.js +25 -0
- package/dist/feature-previews/research-preview.d.ts +33 -0
- package/dist/feature-previews/research-preview.js +115 -0
- package/dist/feature-previews/surface.d.ts +34 -0
- package/dist/feature-previews/surface.js +42 -0
- package/dist/feature-previews/time-tracking-preview.d.ts +35 -0
- package/dist/feature-previews/time-tracking-preview.js +135 -0
- package/dist/task-status.d.ts +21 -0
- package/dist/task-status.js +27 -0
- package/dist/types.d.ts +29 -1
- package/package.json +12 -5
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PreviewMatter, PreviewPerson } from "./mock-data.js";
|
|
2
|
+
import { FeaturePreviewProps } from "./surface.js";
|
|
3
|
+
import { BillingPreview, BillingPreviewData, BillingPreviewEntry, BillingPreviewLabels } from "./billing-preview.js";
|
|
4
|
+
import { ContactsPreview, ContactsPreviewData, ContactsPreviewLabels } from "./contacts-preview.js";
|
|
5
|
+
import { DocumentsPreview, DocumentsPreviewComment, DocumentsPreviewData, DocumentsPreviewLabels } from "./documents-preview.js";
|
|
6
|
+
import { MattersPreview, MattersPreviewData, MattersPreviewLabels, MattersPreviewTask, MattersPreviewTaskStatus } from "./matters-preview.js";
|
|
7
|
+
import { ResearchPreview, ResearchPreviewData, ResearchPreviewDecision, ResearchPreviewLabels } from "./research-preview.js";
|
|
8
|
+
import { TimeTrackingPreview, TimeTrackingPreviewData, TimeTrackingPreviewEntry, TimeTrackingPreviewLabels } from "./time-tracking-preview.js";
|
|
9
|
+
export { BillingPreview, type BillingPreviewData, type BillingPreviewEntry, type BillingPreviewLabels, ContactsPreview, type ContactsPreviewData, type ContactsPreviewLabels, DocumentsPreview, type DocumentsPreviewComment, type DocumentsPreviewData, type DocumentsPreviewLabels, type FeaturePreviewProps, MattersPreview, type MattersPreviewData, type MattersPreviewLabels, type MattersPreviewTask, type MattersPreviewTaskStatus, type PreviewMatter, type PreviewPerson, ResearchPreview, type ResearchPreviewData, type ResearchPreviewDecision, type ResearchPreviewLabels, TimeTrackingPreview, type TimeTrackingPreviewData, type TimeTrackingPreviewEntry, type TimeTrackingPreviewLabels };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BillingPreview } from "./billing-preview.js";
|
|
2
|
+
import { ContactsPreview } from "./contacts-preview.js";
|
|
3
|
+
import { DocumentsPreview } from "./documents-preview.js";
|
|
4
|
+
import { MattersPreview } from "./matters-preview.js";
|
|
5
|
+
import { ResearchPreview } from "./research-preview.js";
|
|
6
|
+
import { TimeTrackingPreview } from "./time-tracking-preview.js";
|
|
7
|
+
export { BillingPreview, ContactsPreview, DocumentsPreview, MattersPreview, ResearchPreview, TimeTrackingPreview };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PreviewMatter, PreviewPerson } from "./mock-data.js";
|
|
2
|
+
import { FeaturePreviewProps } from "./surface.js";
|
|
3
|
+
//#region src/feature-previews/matters-preview.d.ts
|
|
4
|
+
export type MattersPreviewLabels = {
|
|
5
|
+
/** Column names: the task statuses the board groups by. */
|
|
6
|
+
open: string;
|
|
7
|
+
inProgress: string;
|
|
8
|
+
done: string;
|
|
9
|
+
};
|
|
10
|
+
export type MattersPreviewTaskStatus = "open" | "in_progress" | "done";
|
|
11
|
+
export type MattersPreviewTask = {
|
|
12
|
+
name: string;
|
|
13
|
+
status: MattersPreviewTaskStatus;
|
|
14
|
+
/** `YYYY-MM-DD`. */
|
|
15
|
+
due: string;
|
|
16
|
+
assignee: PreviewPerson;
|
|
17
|
+
/** File name of an attached document, if any. */
|
|
18
|
+
attachment: string | null;
|
|
19
|
+
};
|
|
20
|
+
/** The board's content: the matter in the title row, its team, and the tasks
|
|
21
|
+
* (grouped into columns by status, in the given order). */
|
|
22
|
+
export type MattersPreviewData = {
|
|
23
|
+
matter: PreviewMatter;
|
|
24
|
+
team: readonly PreviewPerson[];
|
|
25
|
+
tasks: readonly MattersPreviewTask[];
|
|
26
|
+
};
|
|
27
|
+
/** A matter's task board: three status columns of task cards with due dates,
|
|
28
|
+
* assignees and an attached document. */
|
|
29
|
+
export declare const MattersPreview: ({ labels, locale, data, className }: FeaturePreviewProps<MattersPreviewLabels, MattersPreviewData>) => import("react").JSX.Element;
|
|
30
|
+
//#endregion
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { TASK_STATUS_COLORS, TASK_STATUS_ICONS } from "../task-status.js";
|
|
2
|
+
import { formatPreviewCount, formatPreviewDate } from "./format.js";
|
|
3
|
+
import { PREVIEW_MATTERS, PREVIEW_PEOPLE } from "./mock-data.js";
|
|
4
|
+
import { FeaturePreviewSurface, PreviewAvatar, PreviewAvatarStack } from "./surface.js";
|
|
5
|
+
import { CalendarIcon, FileTextIcon } from "@stll/ui/icons";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { cn } from "@stll/ui/utils";
|
|
8
|
+
import { KanbanCardShell, KanbanColumnHeader } from "@stll/ui/kanban";
|
|
9
|
+
import { MatterIcon } from "@stll/ui/matter-icon";
|
|
10
|
+
//#region src/feature-previews/matters-preview.tsx
|
|
11
|
+
/** A matter's task board: three status columns of task cards with due dates,
|
|
12
|
+
* assignees and an attached document. */
|
|
13
|
+
const MattersPreview = ({ labels, locale, data = DEFAULT_DATA, className }) => /* @__PURE__ */ jsx(FeaturePreviewSurface, {
|
|
14
|
+
className,
|
|
15
|
+
icon: /* @__PURE__ */ jsx(MatterIcon, {
|
|
16
|
+
className: "size-4 shrink-0",
|
|
17
|
+
matter: data.matter
|
|
18
|
+
}),
|
|
19
|
+
meta: /* @__PURE__ */ jsx(PreviewAvatarStack, { people: data.team }),
|
|
20
|
+
title: data.matter.name,
|
|
21
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
22
|
+
className: "grid h-full grid-cols-3 gap-2 p-2",
|
|
23
|
+
children: COLUMNS.map((column) => {
|
|
24
|
+
const tasks = data.tasks.filter((task) => task.status === column.status);
|
|
25
|
+
const Icon = TASK_STATUS_ICONS[column.status];
|
|
26
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
27
|
+
className: "bg-muted/50 flex min-w-0 flex-col gap-1.5 rounded-md pb-1.5",
|
|
28
|
+
children: [/* @__PURE__ */ jsx(KanbanColumnHeader, {
|
|
29
|
+
className: "h-8 px-2",
|
|
30
|
+
meta: formatPreviewCount(locale, tasks.length),
|
|
31
|
+
swatch: /* @__PURE__ */ jsx(Icon, { className: cn("size-3.5 shrink-0", TASK_STATUS_COLORS[column.status]) }),
|
|
32
|
+
title: labels[column.label]
|
|
33
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
34
|
+
className: "flex min-w-0 flex-col gap-1.5 px-1.5",
|
|
35
|
+
children: tasks.map((task) => /* @__PURE__ */ jsx(PreviewTaskCard, {
|
|
36
|
+
locale,
|
|
37
|
+
task
|
|
38
|
+
}, task.name))
|
|
39
|
+
})]
|
|
40
|
+
}, column.status);
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
const PreviewTaskCard = ({ task, locale }) => {
|
|
45
|
+
const Icon = TASK_STATUS_ICONS[task.status];
|
|
46
|
+
return /* @__PURE__ */ jsx(KanbanCardShell, {
|
|
47
|
+
className: "p-2.5",
|
|
48
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
49
|
+
className: "flex flex-col gap-2",
|
|
50
|
+
children: [
|
|
51
|
+
/* @__PURE__ */ jsxs("span", {
|
|
52
|
+
className: "flex min-w-0 items-start gap-1.5 leading-snug font-medium",
|
|
53
|
+
children: [/* @__PURE__ */ jsx(Icon, { className: cn("mt-px size-3.5 shrink-0", TASK_STATUS_COLORS[task.status]) }), /* @__PURE__ */ jsx("span", {
|
|
54
|
+
className: "line-clamp-2 min-w-0",
|
|
55
|
+
children: task.name
|
|
56
|
+
})]
|
|
57
|
+
}),
|
|
58
|
+
task.attachment === null ? null : /* @__PURE__ */ jsxs("span", {
|
|
59
|
+
className: "text-muted-foreground flex min-w-0 items-center gap-1.5",
|
|
60
|
+
children: [/* @__PURE__ */ jsx(FileTextIcon, { className: "size-3 shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
61
|
+
className: "truncate",
|
|
62
|
+
children: task.attachment
|
|
63
|
+
})]
|
|
64
|
+
}),
|
|
65
|
+
/* @__PURE__ */ jsxs("div", {
|
|
66
|
+
className: "text-muted-foreground flex min-w-0 items-center gap-1.5 leading-none",
|
|
67
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
68
|
+
className: "bg-muted/60 flex min-w-0 items-center gap-1 rounded px-1.5 py-0.5 whitespace-nowrap tabular-nums",
|
|
69
|
+
children: [/* @__PURE__ */ jsx(CalendarIcon, { className: "size-3 shrink-0" }), formatPreviewDate({
|
|
70
|
+
locale,
|
|
71
|
+
date: task.due,
|
|
72
|
+
options: {
|
|
73
|
+
day: "numeric",
|
|
74
|
+
month: "short"
|
|
75
|
+
}
|
|
76
|
+
})]
|
|
77
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
78
|
+
className: "ms-auto",
|
|
79
|
+
children: /* @__PURE__ */ jsx(PreviewAvatar, { person: task.assignee })
|
|
80
|
+
})]
|
|
81
|
+
})
|
|
82
|
+
]
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
const COLUMNS = [
|
|
87
|
+
{
|
|
88
|
+
status: "open",
|
|
89
|
+
label: "open"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
status: "in_progress",
|
|
93
|
+
label: "inProgress"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
status: "done",
|
|
97
|
+
label: "done"
|
|
98
|
+
}
|
|
99
|
+
];
|
|
100
|
+
const DEFAULT_DATA = {
|
|
101
|
+
matter: PREVIEW_MATTERS.cargo,
|
|
102
|
+
team: [
|
|
103
|
+
PREVIEW_PEOPLE.laura,
|
|
104
|
+
PREVIEW_PEOPLE.tomas,
|
|
105
|
+
PREVIEW_PEOPLE.amira
|
|
106
|
+
],
|
|
107
|
+
tasks: [
|
|
108
|
+
{
|
|
109
|
+
name: "Draft statement of claim",
|
|
110
|
+
status: "open",
|
|
111
|
+
due: "2026-10-28",
|
|
112
|
+
assignee: PREVIEW_PEOPLE.laura,
|
|
113
|
+
attachment: null
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "Collect shipping manifests",
|
|
117
|
+
status: "open",
|
|
118
|
+
due: "2026-10-30",
|
|
119
|
+
assignee: PREVIEW_PEOPLE.tomas,
|
|
120
|
+
attachment: null
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "Review expert report on cargo damage",
|
|
124
|
+
status: "in_progress",
|
|
125
|
+
due: "2026-10-21",
|
|
126
|
+
assignee: PREVIEW_PEOPLE.amira,
|
|
127
|
+
attachment: "Expert report – cargo damage.pdf"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "Prepare witness statements",
|
|
131
|
+
status: "in_progress",
|
|
132
|
+
due: "2026-10-23",
|
|
133
|
+
assignee: PREVIEW_PEOPLE.laura,
|
|
134
|
+
attachment: null
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "Send letter before action",
|
|
138
|
+
status: "done",
|
|
139
|
+
due: "2026-10-12",
|
|
140
|
+
assignee: PREVIEW_PEOPLE.tomas,
|
|
141
|
+
attachment: "Letter before action.docx"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "Run conflict check",
|
|
145
|
+
status: "done",
|
|
146
|
+
due: "2026-10-02",
|
|
147
|
+
assignee: PREVIEW_PEOPLE.amira,
|
|
148
|
+
attachment: null
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
};
|
|
152
|
+
//#endregion
|
|
153
|
+
export { MattersPreview };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/feature-previews/mock-data.d.ts
|
|
2
|
+
/** A person drawn as initials (assignees, reviewers, comment authors). */
|
|
3
|
+
export type PreviewPerson = {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const PREVIEW_PEOPLE: {
|
|
7
|
+
readonly laura: {
|
|
8
|
+
readonly name: "Laura Chen";
|
|
9
|
+
};
|
|
10
|
+
readonly tomas: {
|
|
11
|
+
readonly name: "Tomás Novák";
|
|
12
|
+
};
|
|
13
|
+
readonly amira: {
|
|
14
|
+
readonly name: "Amira Haddad";
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
/** A matter drawn with its icon. `color` is a matter colour token
|
|
18
|
+
* (`--option-blue`, `--option-violet`, …); `id` seeds the fallback colour. */
|
|
19
|
+
export type PreviewMatter = {
|
|
20
|
+
id: string;
|
|
21
|
+
color: string;
|
|
22
|
+
name: string;
|
|
23
|
+
};
|
|
24
|
+
export declare const PREVIEW_MATTERS: {
|
|
25
|
+
readonly cargo: {
|
|
26
|
+
readonly id: "preview-matter-cargo";
|
|
27
|
+
readonly color: "--option-blue";
|
|
28
|
+
readonly name: "Harbour Logistics v. Meridian Freight";
|
|
29
|
+
};
|
|
30
|
+
readonly acquisition: {
|
|
31
|
+
readonly id: "preview-matter-acquisition";
|
|
32
|
+
readonly color: "--option-violet";
|
|
33
|
+
readonly name: "Project Alder: share purchase";
|
|
34
|
+
};
|
|
35
|
+
readonly lease: {
|
|
36
|
+
readonly id: "preview-matter-lease";
|
|
37
|
+
readonly color: "--option-amber";
|
|
38
|
+
readonly name: "Quayside warehouse lease";
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/feature-previews/mock-data.ts
|
|
2
|
+
const PREVIEW_PEOPLE = {
|
|
3
|
+
laura: { name: "Laura Chen" },
|
|
4
|
+
tomas: { name: "Tomás Novák" },
|
|
5
|
+
amira: { name: "Amira Haddad" }
|
|
6
|
+
};
|
|
7
|
+
const PREVIEW_MATTERS = {
|
|
8
|
+
cargo: {
|
|
9
|
+
id: "preview-matter-cargo",
|
|
10
|
+
color: "--option-blue",
|
|
11
|
+
name: "Harbour Logistics v. Meridian Freight"
|
|
12
|
+
},
|
|
13
|
+
acquisition: {
|
|
14
|
+
id: "preview-matter-acquisition",
|
|
15
|
+
color: "--option-violet",
|
|
16
|
+
name: "Project Alder: share purchase"
|
|
17
|
+
},
|
|
18
|
+
lease: {
|
|
19
|
+
id: "preview-matter-lease",
|
|
20
|
+
color: "--option-amber",
|
|
21
|
+
name: "Quayside warehouse lease"
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { PREVIEW_MATTERS, PREVIEW_PEOPLE };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FeaturePreviewProps } from "./surface.js";
|
|
2
|
+
import { COURT_TIER_WEIGHT } from "@stll/ui/document-identity-badge.logic";
|
|
3
|
+
//#region src/feature-previews/research-preview.d.ts
|
|
4
|
+
export type ResearchPreviewLabels = {
|
|
5
|
+
/** Title of the case-law search surface. */
|
|
6
|
+
caseLaw: string;
|
|
7
|
+
};
|
|
8
|
+
export type ResearchPreviewDecision = {
|
|
9
|
+
court: {
|
|
10
|
+
name: string;
|
|
11
|
+
/** Badge text (`NS`, `ÚS`). */
|
|
12
|
+
abbreviation: string;
|
|
13
|
+
/** Badge weight, from solid (constitutional) to dashed (other). */
|
|
14
|
+
tier: keyof typeof COURT_TIER_WEIGHT;
|
|
15
|
+
};
|
|
16
|
+
/** `YYYY-MM-DD`. */
|
|
17
|
+
date: string;
|
|
18
|
+
/** Unique within the results. */
|
|
19
|
+
fileNumber: string;
|
|
20
|
+
/** The matched passage: `hit` is highlighted between `before` and
|
|
21
|
+
* `after`. */
|
|
22
|
+
before: string;
|
|
23
|
+
hit: string;
|
|
24
|
+
after: string;
|
|
25
|
+
};
|
|
26
|
+
export type ResearchPreviewData = {
|
|
27
|
+
query: string;
|
|
28
|
+
decisions: readonly ResearchPreviewDecision[];
|
|
29
|
+
};
|
|
30
|
+
/** Case-law search: a query and the decisions it found, each with its court,
|
|
31
|
+
* date, file number and the passage that matched. */
|
|
32
|
+
export declare const ResearchPreview: ({ labels, locale, data, className }: FeaturePreviewProps<ResearchPreviewLabels, ResearchPreviewData>) => import("react").JSX.Element;
|
|
33
|
+
//#endregion
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { formatPreviewDate } from "./format.js";
|
|
2
|
+
import { FeaturePreviewSurface } from "./surface.js";
|
|
3
|
+
import { CaseLawIcon, SearchIcon } from "@stll/ui/icons";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { SEARCH_HIT_MARK, TextMark } from "@stll/ui/text-mark";
|
|
6
|
+
import { DocumentIdentityBadge } from "@stll/ui/document-identity-badge";
|
|
7
|
+
//#region src/feature-previews/research-preview.tsx
|
|
8
|
+
/** Case-law search: a query and the decisions it found, each with its court,
|
|
9
|
+
* date, file number and the passage that matched. */
|
|
10
|
+
const ResearchPreview = ({ labels, locale, data = DEFAULT_DATA, className }) => /* @__PURE__ */ jsx(FeaturePreviewSurface, {
|
|
11
|
+
className,
|
|
12
|
+
icon: /* @__PURE__ */ jsx(CaseLawIcon, { className: "text-muted-foreground size-4 shrink-0" }),
|
|
13
|
+
title: labels.caseLaw,
|
|
14
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
15
|
+
className: "flex h-full min-h-0 flex-col gap-2 p-3",
|
|
16
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
17
|
+
className: "bg-card flex h-9 shrink-0 items-center gap-2 rounded-md border px-2.5 text-sm shadow-xs",
|
|
18
|
+
children: [/* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground size-4 shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
19
|
+
className: "truncate",
|
|
20
|
+
children: data.query
|
|
21
|
+
})]
|
|
22
|
+
}), /* @__PURE__ */ jsx("ol", {
|
|
23
|
+
className: "flex min-h-0 flex-col divide-y",
|
|
24
|
+
children: data.decisions.map((decision) => /* @__PURE__ */ jsxs("li", {
|
|
25
|
+
className: "flex flex-col gap-1 py-2.5",
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ jsxs("div", {
|
|
28
|
+
className: "flex min-w-0 items-center gap-2",
|
|
29
|
+
children: [
|
|
30
|
+
/* @__PURE__ */ jsx(DocumentIdentityBadge, { identity: {
|
|
31
|
+
kind: "decision",
|
|
32
|
+
courtAbbreviation: decision.court.abbreviation,
|
|
33
|
+
courtTier: decision.court.tier
|
|
34
|
+
} }),
|
|
35
|
+
/* @__PURE__ */ jsx("span", {
|
|
36
|
+
className: "truncate text-sm font-medium",
|
|
37
|
+
children: decision.court.name
|
|
38
|
+
}),
|
|
39
|
+
/* @__PURE__ */ jsx("span", {
|
|
40
|
+
className: "text-muted-foreground ms-auto shrink-0 whitespace-nowrap tabular-nums",
|
|
41
|
+
children: formatPreviewDate({
|
|
42
|
+
locale,
|
|
43
|
+
date: decision.date,
|
|
44
|
+
options: {
|
|
45
|
+
day: "numeric",
|
|
46
|
+
month: "short",
|
|
47
|
+
year: "numeric"
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
]
|
|
52
|
+
}),
|
|
53
|
+
/* @__PURE__ */ jsx("bdi", {
|
|
54
|
+
className: "text-muted-foreground text-2xs font-mono",
|
|
55
|
+
children: decision.fileNumber
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ jsxs("p", {
|
|
58
|
+
className: "text-foreground-strong-muted line-clamp-2 leading-relaxed",
|
|
59
|
+
children: [
|
|
60
|
+
decision.before,
|
|
61
|
+
/* @__PURE__ */ jsx(TextMark, {
|
|
62
|
+
...SEARCH_HIT_MARK,
|
|
63
|
+
children: decision.hit
|
|
64
|
+
}),
|
|
65
|
+
decision.after
|
|
66
|
+
]
|
|
67
|
+
})
|
|
68
|
+
]
|
|
69
|
+
}, decision.fileNumber))
|
|
70
|
+
})]
|
|
71
|
+
})
|
|
72
|
+
});
|
|
73
|
+
const DEFAULT_DATA = {
|
|
74
|
+
query: "limitation period for warranty claims under a share purchase",
|
|
75
|
+
decisions: [
|
|
76
|
+
{
|
|
77
|
+
court: {
|
|
78
|
+
name: "Supreme Court",
|
|
79
|
+
abbreviation: "NS",
|
|
80
|
+
tier: "supreme"
|
|
81
|
+
},
|
|
82
|
+
date: "2025-03-18",
|
|
83
|
+
fileNumber: "27 Cdo 1931/2024",
|
|
84
|
+
before: "For a claim under a share purchase agreement, the ",
|
|
85
|
+
hit: "limitation period",
|
|
86
|
+
after: " begins when the buyer learned of the breach of warranty, not on the day of completion."
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
court: {
|
|
90
|
+
name: "Constitutional Court",
|
|
91
|
+
abbreviation: "ÚS",
|
|
92
|
+
tier: "constitutional"
|
|
93
|
+
},
|
|
94
|
+
date: "2024-06-04",
|
|
95
|
+
fileNumber: "II. ÚS 1187/23",
|
|
96
|
+
before: "A court that applies the ",
|
|
97
|
+
hit: "limitation period",
|
|
98
|
+
after: " without asking when the claimant could know of the defect denies a fair hearing."
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
court: {
|
|
102
|
+
name: "High Court in Prague",
|
|
103
|
+
abbreviation: "VS",
|
|
104
|
+
tier: "regional"
|
|
105
|
+
},
|
|
106
|
+
date: "2023-11-29",
|
|
107
|
+
fileNumber: "5 Cmo 112/2023",
|
|
108
|
+
before: "Contractual caps on ",
|
|
109
|
+
hit: "warranty claims",
|
|
110
|
+
after: " do not shorten the statutory limitation period unless the parties say so expressly."
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
};
|
|
114
|
+
//#endregion
|
|
115
|
+
export { ResearchPreview };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PreviewPerson } from "./mock-data.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
//#region src/feature-previews/surface.d.ts
|
|
4
|
+
/** Props every scene takes: its interface labels, the formatting locale for
|
|
5
|
+
* its dates and numbers, its content, and classes for the host's sizing. */
|
|
6
|
+
export type FeaturePreviewProps<Labels, Data> = {
|
|
7
|
+
labels: Labels;
|
|
8
|
+
/** Full formatting locale (`en-GB`, `cs-CZ`), used for dates and numbers. */
|
|
9
|
+
locale: string;
|
|
10
|
+
/** What the scene depicts (matters, documents, amounts). Omitted, the
|
|
11
|
+
* scene shows the package's fictional English set. */
|
|
12
|
+
data?: Data | undefined;
|
|
13
|
+
className?: string | undefined;
|
|
14
|
+
};
|
|
15
|
+
type FeaturePreviewSurfaceProps = React.PropsWithChildren<{
|
|
16
|
+
icon: React.ReactNode;
|
|
17
|
+
title: React.ReactNode;
|
|
18
|
+
meta?: React.ReactNode;
|
|
19
|
+
className?: string | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* The window every scene draws in: a title row and a clipped body that fills
|
|
23
|
+
* whatever box the host gives it. Decorative, so it is hidden from assistive
|
|
24
|
+
* technology and takes no pointer input; the host names what it illustrates.
|
|
25
|
+
*/
|
|
26
|
+
export declare const FeaturePreviewSurface: ({ icon, title, meta, className, children }: FeaturePreviewSurfaceProps) => React.JSX.Element;
|
|
27
|
+
/** A row of overlapping initials, as the app shows a matter's team. */
|
|
28
|
+
export declare const PreviewAvatarStack: ({ people }: {
|
|
29
|
+
people: readonly PreviewPerson[];
|
|
30
|
+
}) => React.JSX.Element;
|
|
31
|
+
export declare const PreviewAvatar: ({ person }: {
|
|
32
|
+
person: PreviewPerson;
|
|
33
|
+
}) => React.JSX.Element;
|
|
34
|
+
//#endregion
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "@stll/ui/utils";
|
|
3
|
+
import { ReviewAuthorAvatar } from "@stll/ui/review-author-avatar";
|
|
4
|
+
//#region src/feature-previews/surface.tsx
|
|
5
|
+
/**
|
|
6
|
+
* The window every scene draws in: a title row and a clipped body that fills
|
|
7
|
+
* whatever box the host gives it. Decorative, so it is hidden from assistive
|
|
8
|
+
* technology and takes no pointer input; the host names what it illustrates.
|
|
9
|
+
*/
|
|
10
|
+
const FeaturePreviewSurface = ({ icon, title, meta, className, children }) => /* @__PURE__ */ jsxs("div", {
|
|
11
|
+
"aria-hidden": true,
|
|
12
|
+
className: cn("bg-background text-foreground pointer-events-none flex size-full min-h-0 flex-col overflow-hidden rounded-lg border text-start text-xs select-none", className),
|
|
13
|
+
"data-slot": "feature-preview",
|
|
14
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
15
|
+
className: "flex h-10 shrink-0 items-center gap-2 border-b px-3",
|
|
16
|
+
children: [
|
|
17
|
+
icon,
|
|
18
|
+
/* @__PURE__ */ jsx("span", {
|
|
19
|
+
className: "min-w-0 truncate text-sm font-medium",
|
|
20
|
+
children: title
|
|
21
|
+
}),
|
|
22
|
+
meta === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
23
|
+
className: "text-muted-foreground ms-auto flex shrink-0 items-center gap-2",
|
|
24
|
+
children: meta
|
|
25
|
+
})
|
|
26
|
+
]
|
|
27
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
28
|
+
className: "relative min-h-0 flex-1 overflow-hidden",
|
|
29
|
+
children
|
|
30
|
+
})]
|
|
31
|
+
});
|
|
32
|
+
/** A row of overlapping initials, as the app shows a matter's team. */
|
|
33
|
+
const PreviewAvatarStack = ({ people }) => /* @__PURE__ */ jsx("span", {
|
|
34
|
+
className: "flex gap-1",
|
|
35
|
+
children: people.map((person) => /* @__PURE__ */ jsx(PreviewAvatar, { person }, person.name))
|
|
36
|
+
});
|
|
37
|
+
const PreviewAvatar = ({ person }) => /* @__PURE__ */ jsx(ReviewAuthorAvatar, {
|
|
38
|
+
className: "size-5 shrink-0",
|
|
39
|
+
name: person.name
|
|
40
|
+
});
|
|
41
|
+
//#endregion
|
|
42
|
+
export { FeaturePreviewSurface, PreviewAvatar, PreviewAvatarStack };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PreviewMatter } from "./mock-data.js";
|
|
2
|
+
import { FeaturePreviewProps } from "./surface.js";
|
|
3
|
+
//#region src/feature-previews/time-tracking-preview.d.ts
|
|
4
|
+
export type TimeTrackingPreviewLabels = {
|
|
5
|
+
/** Title of the time-tracking surface. */
|
|
6
|
+
time: string;
|
|
7
|
+
/** Label of the day's total row. */
|
|
8
|
+
summary: string;
|
|
9
|
+
};
|
|
10
|
+
export type TimeTrackingPreviewEntry = {
|
|
11
|
+
/** `HH:MM`; `start` is unique within the day. */
|
|
12
|
+
start: string;
|
|
13
|
+
end: string;
|
|
14
|
+
minutes: number;
|
|
15
|
+
description: string;
|
|
16
|
+
matter: PreviewMatter;
|
|
17
|
+
};
|
|
18
|
+
/** The day's content: the running timer (its elapsed time frozen, since a
|
|
19
|
+
* static scene shows one moment) and the entries already recorded. The
|
|
20
|
+
* day's total is computed. */
|
|
21
|
+
export type TimeTrackingPreviewData = {
|
|
22
|
+
/** `YYYY-MM-DD`. */
|
|
23
|
+
day: string;
|
|
24
|
+
running: {
|
|
25
|
+
description: string;
|
|
26
|
+
matter: PreviewMatter;
|
|
27
|
+
elapsedMinutes: number;
|
|
28
|
+
};
|
|
29
|
+
/** In the order they were worked. */
|
|
30
|
+
entries: readonly TimeTrackingPreviewEntry[];
|
|
31
|
+
};
|
|
32
|
+
/** A running timer on a matter above the day's recorded entries and their
|
|
33
|
+
* total. */
|
|
34
|
+
export declare const TimeTrackingPreview: ({ labels, locale, data: { day, running, entries }, className }: FeaturePreviewProps<TimeTrackingPreviewLabels, TimeTrackingPreviewData>) => import("react").JSX.Element;
|
|
35
|
+
//#endregion
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { formatPreviewDate, formatPreviewDuration, formatPreviewTime } from "./format.js";
|
|
2
|
+
import { PREVIEW_MATTERS } from "./mock-data.js";
|
|
3
|
+
import { FeaturePreviewSurface } from "./surface.js";
|
|
4
|
+
import { ClockIcon, SquareIcon } from "@stll/ui/icons";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { MatterIcon } from "@stll/ui/matter-icon";
|
|
7
|
+
//#region src/feature-previews/time-tracking-preview.tsx
|
|
8
|
+
/** A running timer on a matter above the day's recorded entries and their
|
|
9
|
+
* total. */
|
|
10
|
+
const TimeTrackingPreview = ({ labels, locale, data: { day, running, entries } = DEFAULT_DATA, className }) => {
|
|
11
|
+
const totalMinutes = entries.reduce((sum, entry) => sum + entry.minutes, 0) + running.elapsedMinutes;
|
|
12
|
+
return /* @__PURE__ */ jsx(FeaturePreviewSurface, {
|
|
13
|
+
className,
|
|
14
|
+
icon: /* @__PURE__ */ jsx(ClockIcon, { className: "text-muted-foreground size-4 shrink-0" }),
|
|
15
|
+
meta: /* @__PURE__ */ jsx("span", {
|
|
16
|
+
className: "whitespace-nowrap",
|
|
17
|
+
children: formatPreviewDate({
|
|
18
|
+
locale,
|
|
19
|
+
date: day,
|
|
20
|
+
options: {
|
|
21
|
+
weekday: "long",
|
|
22
|
+
day: "numeric",
|
|
23
|
+
month: "long"
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
}),
|
|
27
|
+
title: labels.time,
|
|
28
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
29
|
+
className: "flex h-full min-h-0 flex-col gap-3 p-3",
|
|
30
|
+
children: [
|
|
31
|
+
/* @__PURE__ */ jsxs("div", {
|
|
32
|
+
className: "bg-card flex min-w-0 items-center gap-3 rounded-lg border p-3 shadow-xs",
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ jsxs("span", {
|
|
35
|
+
className: "relative flex size-2 shrink-0",
|
|
36
|
+
children: [/* @__PURE__ */ jsx("span", { className: "bg-success absolute inset-0 rounded-full opacity-60 motion-safe:animate-ping" }), /* @__PURE__ */ jsx("span", { className: "bg-success relative size-2 rounded-full" })]
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ jsxs("div", {
|
|
39
|
+
className: "flex min-w-0 flex-1 flex-col gap-1",
|
|
40
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
41
|
+
className: "truncate text-sm font-medium",
|
|
42
|
+
children: running.description
|
|
43
|
+
}), /* @__PURE__ */ jsx(PreviewMatterLabel, { matter: running.matter })]
|
|
44
|
+
}),
|
|
45
|
+
/* @__PURE__ */ jsx("span", {
|
|
46
|
+
className: "shrink-0 text-xl font-medium tabular-nums",
|
|
47
|
+
children: formatPreviewDuration(locale, running.elapsedMinutes)
|
|
48
|
+
}),
|
|
49
|
+
/* @__PURE__ */ jsx("span", {
|
|
50
|
+
className: "bg-primary text-primary-foreground flex size-7 shrink-0 items-center justify-center rounded-md",
|
|
51
|
+
children: /* @__PURE__ */ jsx(SquareIcon, { className: "size-3 fill-current" })
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
}),
|
|
55
|
+
/* @__PURE__ */ jsx("ul", {
|
|
56
|
+
className: "flex min-h-0 flex-col divide-y",
|
|
57
|
+
children: entries.map((entry) => /* @__PURE__ */ jsxs("li", {
|
|
58
|
+
className: "flex min-w-0 items-center gap-3 py-2",
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ jsxs("span", {
|
|
61
|
+
className: "text-muted-foreground w-24 shrink-0 whitespace-nowrap tabular-nums",
|
|
62
|
+
children: [
|
|
63
|
+
formatPreviewTime(locale, entry.start),
|
|
64
|
+
"–",
|
|
65
|
+
formatPreviewTime(locale, entry.end)
|
|
66
|
+
]
|
|
67
|
+
}),
|
|
68
|
+
/* @__PURE__ */ jsxs("div", {
|
|
69
|
+
className: "flex min-w-0 flex-1 flex-col gap-0.5",
|
|
70
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
71
|
+
className: "truncate",
|
|
72
|
+
children: entry.description
|
|
73
|
+
}), /* @__PURE__ */ jsx(PreviewMatterLabel, { matter: entry.matter })]
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ jsx("span", {
|
|
76
|
+
className: "shrink-0 tabular-nums",
|
|
77
|
+
children: formatPreviewDuration(locale, entry.minutes)
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
}, entry.start))
|
|
81
|
+
}),
|
|
82
|
+
/* @__PURE__ */ jsxs("div", {
|
|
83
|
+
className: "flex items-center justify-between border-t pt-2 text-sm font-semibold",
|
|
84
|
+
children: [/* @__PURE__ */ jsx("span", { children: labels.summary }), /* @__PURE__ */ jsx("span", {
|
|
85
|
+
className: "tabular-nums",
|
|
86
|
+
children: formatPreviewDuration(locale, totalMinutes)
|
|
87
|
+
})]
|
|
88
|
+
})
|
|
89
|
+
]
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
const PreviewMatterLabel = ({ matter }) => /* @__PURE__ */ jsxs("span", {
|
|
94
|
+
className: "text-muted-foreground flex min-w-0 items-center gap-1.5",
|
|
95
|
+
children: [/* @__PURE__ */ jsx(MatterIcon, {
|
|
96
|
+
className: "size-3 shrink-0",
|
|
97
|
+
matter
|
|
98
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
99
|
+
className: "truncate",
|
|
100
|
+
children: matter.name
|
|
101
|
+
})]
|
|
102
|
+
});
|
|
103
|
+
const DEFAULT_DATA = {
|
|
104
|
+
day: "2026-10-19",
|
|
105
|
+
running: {
|
|
106
|
+
description: "Prepare witness statements",
|
|
107
|
+
matter: PREVIEW_MATTERS.cargo,
|
|
108
|
+
elapsedMinutes: 84
|
|
109
|
+
},
|
|
110
|
+
entries: [
|
|
111
|
+
{
|
|
112
|
+
start: "08:30",
|
|
113
|
+
end: "09:15",
|
|
114
|
+
minutes: 45,
|
|
115
|
+
description: "Review disclosure list",
|
|
116
|
+
matter: PREVIEW_MATTERS.cargo
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
start: "09:30",
|
|
120
|
+
end: "11:00",
|
|
121
|
+
minutes: 90,
|
|
122
|
+
description: "Mark up share purchase agreement",
|
|
123
|
+
matter: PREVIEW_MATTERS.acquisition
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
start: "11:15",
|
|
127
|
+
end: "11:45",
|
|
128
|
+
minutes: 30,
|
|
129
|
+
description: "Call with landlord's solicitors",
|
|
130
|
+
matter: PREVIEW_MATTERS.lease
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
};
|
|
134
|
+
//#endregion
|
|
135
|
+
export { TimeTrackingPreview };
|