@flamingo-stack/openframe-frontend-core 0.0.563 → 0.0.564-1861.5029.1
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/{chunk-ZYALXBYA.js → chunk-2FYF37J3.js} +2 -2
- package/dist/{chunk-HORO3E6J.js → chunk-AHALU42N.js} +2 -2
- package/dist/{chunk-O2QXZ3CG.js → chunk-CZBZBK7M.js} +4 -4
- package/dist/{chunk-WNTNRQEL.cjs → chunk-DL5DXWJC.cjs} +20 -20
- package/dist/{chunk-WNTNRQEL.cjs.map → chunk-DL5DXWJC.cjs.map} +1 -1
- package/dist/{chunk-TVG2FHQX.js → chunk-E7DXYEHE.js} +2 -2
- package/dist/{chunk-2G65HZUS.cjs → chunk-EC6L6JMJ.cjs} +3 -3
- package/dist/{chunk-2G65HZUS.cjs.map → chunk-EC6L6JMJ.cjs.map} +1 -1
- package/dist/{chunk-HGSDDB2K.cjs → chunk-EZBTXXKD.cjs} +14 -14
- package/dist/{chunk-HGSDDB2K.cjs.map → chunk-EZBTXXKD.cjs.map} +1 -1
- package/dist/{chunk-D2C6VCH2.cjs → chunk-FN3RUX3O.cjs} +7 -7
- package/dist/{chunk-D2C6VCH2.cjs.map → chunk-FN3RUX3O.cjs.map} +1 -1
- package/dist/{chunk-J47EIBVD.js → chunk-IQJLPMM2.js} +2 -2
- package/dist/{chunk-IZ5NSAOY.js → chunk-KH2MXP6G.js} +2 -2
- package/dist/{chunk-DWOFOVKK.js → chunk-KYO5Z4NJ.js} +2 -2
- package/dist/{chunk-VOD7HME2.cjs → chunk-M4L5CPIC.cjs} +38 -5
- package/dist/chunk-M4L5CPIC.cjs.map +1 -0
- package/dist/{chunk-AQKAKYLA.js → chunk-MOEVW5IF.js} +37 -4
- package/dist/chunk-MOEVW5IF.js.map +1 -0
- package/dist/{chunk-EAUQ7FVQ.cjs → chunk-NQ5HGG3C.cjs} +26 -26
- package/dist/{chunk-EAUQ7FVQ.cjs.map → chunk-NQ5HGG3C.cjs.map} +1 -1
- package/dist/{chunk-WFMF7YCC.js → chunk-O22EZW7V.js} +2 -2
- package/dist/{chunk-4AZVFEFJ.js → chunk-O2XIWDKK.js} +5 -5
- package/dist/{chunk-6KBIGWCX.cjs → chunk-OKBNMR2M.cjs} +31 -31
- package/dist/{chunk-6KBIGWCX.cjs.map → chunk-OKBNMR2M.cjs.map} +1 -1
- package/dist/{chunk-ZGE45I4O.cjs → chunk-PLNDJLSK.cjs} +11 -11
- package/dist/{chunk-ZGE45I4O.cjs.map → chunk-PLNDJLSK.cjs.map} +1 -1
- package/dist/{chunk-475LZFVJ.cjs → chunk-PXZBRYIB.cjs} +39 -39
- package/dist/{chunk-475LZFVJ.cjs.map → chunk-PXZBRYIB.cjs.map} +1 -1
- package/dist/{chunk-KNGTLGTV.cjs → chunk-R5O54P4L.cjs} +5 -5
- package/dist/{chunk-KNGTLGTV.cjs.map → chunk-R5O54P4L.cjs.map} +1 -1
- package/dist/{chunk-VTRHCB5Y.js → chunk-TH4TNYFM.js} +4 -4
- package/dist/{chunk-A27PO3EI.js → chunk-U5XBD34S.js} +6 -6
- package/dist/{chunk-S7UOFXEB.cjs → chunk-URJSFWPH.cjs} +9 -9
- package/dist/{chunk-S7UOFXEB.cjs.map → chunk-URJSFWPH.cjs.map} +1 -1
- package/dist/{chunk-WPBMH6JX.js → chunk-WWNQFZ7R.js} +2 -2
- package/dist/{chunk-GMAA6A3V.js → chunk-XEQBE6RN.js} +2 -2
- package/dist/{chunk-LVM3KJJN.cjs → chunk-XL3HIB3C.cjs} +62 -62
- package/dist/{chunk-LVM3KJJN.cjs.map → chunk-XL3HIB3C.cjs.map} +1 -1
- package/dist/{chunk-VOSOLVXC.cjs → chunk-YOI3SIVL.cjs} +4 -4
- package/dist/{chunk-VOSOLVXC.cjs.map → chunk-YOI3SIVL.cjs.map} +1 -1
- package/dist/{chunk-SDCFVKFA.cjs → chunk-Z4WGVDF3.cjs} +86 -86
- package/dist/{chunk-SDCFVKFA.cjs.map → chunk-Z4WGVDF3.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/entity-cards/employee-entry-card.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/comment-card.d.ts +11 -2
- package/dist/components/comment-card.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/clickup-tasks-manager.d.ts +6 -1
- package/dist/components/features/clickup-tasks-manager.d.ts.map +1 -1
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/form.d.ts +1 -1
- package/dist/components/form.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +22 -22
- package/dist/components/help-center-pages/index.js +13 -13
- package/dist/components/index.cjs +210 -197
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +90 -77
- package/dist/components/index.js.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +30 -30
- package/dist/components/meeting-scheduler/index.js +3 -3
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/hooks/use-onboarding-guides.d.ts +3 -3
- package/dist/components/onboarding-guides/hooks/use-onboarding-guides.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +6 -6
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +1 -1
- package/dist/hooks/use-meeting-booking.d.ts +1 -1
- package/dist/index.cjs +16 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +15 -1
- package/dist/types/department.d.ts +36 -0
- package/dist/types/department.d.ts.map +1 -0
- package/dist/types/design-doc.d.ts +242 -0
- package/dist/types/design-doc.d.ts.map +1 -0
- package/dist/types/employee.d.ts +6 -2
- package/dist/types/employee.d.ts.map +1 -1
- package/dist/types/index.cjs +16 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +10 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/marketing.d.ts +1 -1
- package/dist/types/marketing.d.ts.map +1 -1
- package/dist/types/profile.d.ts +2 -1
- package/dist/types/profile.d.ts.map +1 -1
- package/dist/types/team.d.ts +7 -1
- package/dist/types/team.d.ts.map +1 -1
- package/dist/types/user.d.ts +3 -1
- package/dist/types/user.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/entity-cards/employee-entry-card.tsx +6 -0
- package/src/components/comment-card.tsx +32 -3
- package/src/components/features/clickup-tasks-manager.tsx +8 -1
- package/src/components/ui/markdown/rich/__tests__/shortcodes.test.ts +47 -0
- package/src/components/ui/markdown/rich/shortcodes.ts +11 -0
- package/src/types/department.ts +39 -0
- package/src/types/design-doc.ts +284 -0
- package/src/types/employee.ts +7 -2
- package/src/types/index.ts +2 -0
- package/src/types/marketing.ts +1 -0
- package/src/types/profile.ts +3 -1
- package/src/types/team.ts +9 -1
- package/src/types/user.ts +4 -1
- package/dist/chunk-AQKAKYLA.js.map +0 -1
- package/dist/chunk-VOD7HME2.cjs.map +0 -1
- /package/dist/{chunk-ZYALXBYA.js.map → chunk-2FYF37J3.js.map} +0 -0
- /package/dist/{chunk-HORO3E6J.js.map → chunk-AHALU42N.js.map} +0 -0
- /package/dist/{chunk-O2QXZ3CG.js.map → chunk-CZBZBK7M.js.map} +0 -0
- /package/dist/{chunk-TVG2FHQX.js.map → chunk-E7DXYEHE.js.map} +0 -0
- /package/dist/{chunk-J47EIBVD.js.map → chunk-IQJLPMM2.js.map} +0 -0
- /package/dist/{chunk-IZ5NSAOY.js.map → chunk-KH2MXP6G.js.map} +0 -0
- /package/dist/{chunk-DWOFOVKK.js.map → chunk-KYO5Z4NJ.js.map} +0 -0
- /package/dist/{chunk-WFMF7YCC.js.map → chunk-O22EZW7V.js.map} +0 -0
- /package/dist/{chunk-4AZVFEFJ.js.map → chunk-O2XIWDKK.js.map} +0 -0
- /package/dist/{chunk-VTRHCB5Y.js.map → chunk-TH4TNYFM.js.map} +0 -0
- /package/dist/{chunk-A27PO3EI.js.map → chunk-U5XBD34S.js.map} +0 -0
- /package/dist/{chunk-WPBMH6JX.js.map → chunk-WWNQFZ7R.js.map} +0 -0
- /package/dist/{chunk-GMAA6A3V.js.map → chunk-XEQBE6RN.js.map} +0 -0
package/dist/types/user.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Types related to users
|
|
3
3
|
*/
|
|
4
|
+
import type { DepartmentRef } from './department';
|
|
4
5
|
/**
|
|
5
6
|
* User profile
|
|
6
7
|
*/
|
|
@@ -13,7 +14,8 @@ export type UserProfile = {
|
|
|
13
14
|
company: string | null;
|
|
14
15
|
bio: string | null;
|
|
15
16
|
employee_id: string | null;
|
|
16
|
-
|
|
17
|
+
department_id: string | null;
|
|
18
|
+
department: DepartmentRef | null;
|
|
17
19
|
role: 'user' | 'super_admin';
|
|
18
20
|
msp_id: string | null;
|
|
19
21
|
created_at: string;
|
package/dist/types/user.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/types/user.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/types/user.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEjD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,UAAU,EAAE,aAAa,GAAG,IAAI,CAAA;IAChC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAA;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,aAAa,EAAE,QAAQ,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAA;IAC/H,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAA;CACtC,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;IAClC,mBAAmB,EAAE,OAAO,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA"}
|
package/package.json
CHANGED
|
@@ -38,6 +38,12 @@ const STATUS_BADGE_CLASS: Record<string, string> = {
|
|
|
38
38
|
published: 'bg-ods-success-secondary text-ods-success',
|
|
39
39
|
draft: 'bg-ods-warning-secondary text-ods-warning',
|
|
40
40
|
archived: 'bg-ods-border text-ods-text-secondary',
|
|
41
|
+
// Design-doc lifecycle (product-hub) — same ODS pairs, no new palette.
|
|
42
|
+
in_review: 'bg-ods-warning-secondary text-ods-warning',
|
|
43
|
+
approved: 'bg-ods-success-secondary text-ods-success',
|
|
44
|
+
building: 'bg-ods-success-secondary text-ods-success',
|
|
45
|
+
shipped: 'bg-ods-success-secondary text-ods-success',
|
|
46
|
+
abandoned: 'bg-ods-border text-ods-text-secondary',
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
/** Shared badge treatment so an entity's own badges (discipline, level) sit
|
|
@@ -37,7 +37,16 @@ interface CommentCardProps {
|
|
|
37
37
|
onDeleteComment?: (commentId: string) => void
|
|
38
38
|
showVendorInfo?: boolean
|
|
39
39
|
compact?: boolean
|
|
40
|
-
|
|
40
|
+
/**
|
|
41
|
+
* `profile` / `vendor` = the OpenMSP comment sections (fixed-height list tiles).
|
|
42
|
+
* `design_doc` = the product-hub design-doc thread: full-height body, no vendor
|
|
43
|
+
* chrome, deletability decided by the caller via `comment.canDelete`.
|
|
44
|
+
*/
|
|
45
|
+
context: 'profile' | 'vendor' | 'design_doc'
|
|
46
|
+
/** Rendered under the body — badges + per-surface actions (design-doc threads). */
|
|
47
|
+
footer?: React.ReactNode
|
|
48
|
+
/** Rendered after the card — nested replies + reply composer (design-doc threads). */
|
|
49
|
+
children?: React.ReactNode
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
export function CommentCard({
|
|
@@ -46,7 +55,9 @@ export function CommentCard({
|
|
|
46
55
|
onDeleteComment,
|
|
47
56
|
showVendorInfo = true,
|
|
48
57
|
compact = false,
|
|
49
|
-
context = 'profile'
|
|
58
|
+
context = 'profile',
|
|
59
|
+
footer,
|
|
60
|
+
children
|
|
50
61
|
}: CommentCardProps) {
|
|
51
62
|
const { user: currentUser } = useAuth()
|
|
52
63
|
|
|
@@ -69,13 +80,21 @@ export function CommentCard({
|
|
|
69
80
|
if (context === 'vendor' && comment.user) {
|
|
70
81
|
return currentUser.id === comment.user.id
|
|
71
82
|
}
|
|
83
|
+
|
|
84
|
+
// Design-doc threads: ownership (own-or-DRI) is computed by the caller with the
|
|
85
|
+
// shared gate rule and handed in as `canDelete`.
|
|
86
|
+
if (context === 'design_doc') {
|
|
87
|
+
return comment.canDelete === true
|
|
88
|
+
}
|
|
72
89
|
|
|
73
90
|
return false
|
|
74
91
|
}
|
|
75
92
|
|
|
76
93
|
const showDeleteButton = canUserDeleteComment()
|
|
94
|
+
const isThread = context === 'design_doc'
|
|
77
95
|
|
|
78
96
|
return (
|
|
97
|
+
<>
|
|
79
98
|
<div className="bg-ods-card border border-ods-border rounded-lg p-4 hover:border-ods-accent transition-all group overflow-hidden w-full max-w-full box-border" style={{ maxWidth: '100%', wordBreak: 'break-word' }}>
|
|
80
99
|
{/* Comment Header */}
|
|
81
100
|
<div className="flex flex-col gap-3 mb-3 min-[420px]:flex-row min-[420px]:items-center min-[420px]:justify-between min-[420px]:gap-2 w-full">
|
|
@@ -94,7 +113,7 @@ export function CommentCard({
|
|
|
94
113
|
{formatActivityTime(comment.createdAt)}
|
|
95
114
|
</span>
|
|
96
115
|
</>
|
|
97
|
-
) : context === 'vendor' && comment.user ? (
|
|
116
|
+
) : (context === 'vendor' || isThread) && comment.user ? (
|
|
98
117
|
<UserSummary
|
|
99
118
|
name={comment.user.name}
|
|
100
119
|
email=""
|
|
@@ -152,6 +171,12 @@ export function CommentCard({
|
|
|
152
171
|
</h4>
|
|
153
172
|
</div>
|
|
154
173
|
)}
|
|
174
|
+
{isThread ? (
|
|
175
|
+
// Thread bodies are read in full — no fixed height, no clamp.
|
|
176
|
+
<p className="text-h4 text-ods-text-primary whitespace-pre-wrap" style={{ wordBreak: 'break-word', overflowWrap: 'break-word', maxWidth: '100%' }}>
|
|
177
|
+
{comment.content}
|
|
178
|
+
</p>
|
|
179
|
+
) : (
|
|
155
180
|
<div className={compact ? "h-[60px] flex items-center" : "h-[72px] flex items-center"}>
|
|
156
181
|
<p className="text-h4 text-ods-text-primary line-clamp-3" style={{
|
|
157
182
|
wordBreak: 'break-word',
|
|
@@ -166,7 +191,11 @@ export function CommentCard({
|
|
|
166
191
|
{comment.content}
|
|
167
192
|
</p>
|
|
168
193
|
</div>
|
|
194
|
+
)}
|
|
195
|
+
{footer ? <div className="pt-2 flex flex-wrap items-center gap-2">{footer}</div> : null}
|
|
169
196
|
</div>
|
|
170
197
|
</div>
|
|
198
|
+
{children}
|
|
199
|
+
</>
|
|
171
200
|
)
|
|
172
201
|
}
|
|
@@ -14,13 +14,19 @@ interface ClickUpTasksManagerProps {
|
|
|
14
14
|
onChange: (tasks: ClickUpTaskLink[]) => void;
|
|
15
15
|
title?: ReactNode; // Support string or ReactNode for badge integration
|
|
16
16
|
className?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Per-row label renderer (passthrough to ArrayEntryManager) — lets a host
|
|
19
|
+
* hydrate the bare task id with the mirror's title/status row.
|
|
20
|
+
*/
|
|
21
|
+
renderLabel?: (item: ClickUpTaskLink, index: number) => ReactNode;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
export function ClickUpTasksManager({
|
|
20
25
|
tasks,
|
|
21
26
|
onChange,
|
|
22
27
|
title = 'ClickUp Roadmap Tasks', // Default title
|
|
23
|
-
className = ''
|
|
28
|
+
className = '',
|
|
29
|
+
renderLabel
|
|
24
30
|
}: ClickUpTasksManagerProps) {
|
|
25
31
|
return (
|
|
26
32
|
<ArrayEntryManager
|
|
@@ -33,6 +39,7 @@ export function ClickUpTasksManager({
|
|
|
33
39
|
addButtonText="Add Task"
|
|
34
40
|
icon={<Lightbulb className="w-5 h-5 text-ods-text-secondary" />}
|
|
35
41
|
className={className}
|
|
42
|
+
renderLabel={renderLabel}
|
|
36
43
|
/>
|
|
37
44
|
);
|
|
38
45
|
}
|
|
@@ -56,3 +56,50 @@ describe('processShortcodes script stripping', () => {
|
|
|
56
56
|
expect(output).toContain('<script>');
|
|
57
57
|
});
|
|
58
58
|
});
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Design-doc embeds (R11): the spec's `{{figma:FILE_KEY[:NODE_ID]}}` grammar
|
|
62
|
+
* is rewritten onto the canonical `{{figma:URL}}` rule, and the two Claude
|
|
63
|
+
* shortcodes alias the OG link card because claude.ai serves
|
|
64
|
+
* `frame-ancestors 'self'` (it cannot be framed — verified 2026-08-23).
|
|
65
|
+
*/
|
|
66
|
+
describe('processShortcodes design-doc embeds', () => {
|
|
67
|
+
it('rewrites {{figma:KEY:NODE}} to the canonical design URL and renders the figma embed', () => {
|
|
68
|
+
const output = processShortcodes('{{figma:aB3xK9:12-405}}');
|
|
69
|
+
expect(output).toContain('class="figma-embed"');
|
|
70
|
+
expect(output).toContain('data-figma-url="https://www.figma.com/design/aB3xK9?node-id=12-405"');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('accepts a colon-separated node id and a bare key', () => {
|
|
74
|
+
expect(processShortcodes('{{figma:aB3xK9:12:405}}')).toContain('node-id=12-405');
|
|
75
|
+
expect(processShortcodes('{{figma:aB3xK9}}')).toContain('data-figma-url="https://www.figma.com/design/aB3xK9"');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('leaves the existing {{figma:URL}} grammar untouched', () => {
|
|
79
|
+
const output = processShortcodes('{{figma:https://www.figma.com/proto/aB3xK9/Flows?node-id=1-2}}');
|
|
80
|
+
expect(output).toContain('data-figma-url="https://www.figma.com/proto/aB3xK9/Flows?node-id=1-2"');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('renders {{claude-artifact:URL}} and {{claude-design:URL}} as link-preview cards, never iframes', () => {
|
|
84
|
+
const artifact = processShortcodes('{{claude-artifact:https://claude.ai/public/artifacts/abc}}');
|
|
85
|
+
const design = processShortcodes('{{claude-design:https://claude.ai/design/xyz}}');
|
|
86
|
+
for (const out of [artifact, design]) {
|
|
87
|
+
expect(out).toContain('class="link-preview"');
|
|
88
|
+
expect(out).not.toContain('<iframe');
|
|
89
|
+
}
|
|
90
|
+
expect(artifact).toContain('data-url="https://claude.ai/public/artifacts/abc"');
|
|
91
|
+
expect(design).toContain('data-url="https://claude.ai/design/xyz"');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('attribute-escapes the claude URL like every other shortcode', () => {
|
|
95
|
+
const output = processShortcodes('{{claude-artifact:https://claude.ai/a?x="1"}}');
|
|
96
|
+
expect(output).toContain('data-url="https://claude.ai/a?x="1""');
|
|
97
|
+
expect(output).not.toContain('x="1"');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('is rewritten inside a code fence exactly like {{link:}} is (parity, not fence-safety)', () => {
|
|
101
|
+
const link = processShortcodes('```\n{{link:https://example.com}}\n```');
|
|
102
|
+
const claude = processShortcodes('```\n{{claude-artifact:https://claude.ai/x}}\n```');
|
|
103
|
+
expect(claude.includes('link-preview')).toBe(link.includes('link-preview'));
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -55,6 +55,12 @@ export const processShortcodes = (content: string): string => {
|
|
|
55
55
|
: `https://twitter.com/twitter/status/${tweetInput}`;
|
|
56
56
|
return `\n\n<div class="tweet-embed" data-tweet-url="${escapeAttr(tweetUrl)}"></div>\n\n`;
|
|
57
57
|
})
|
|
58
|
+
// Figma by file key (spec grammar): {{figma:FILE_KEY[:NODE_ID]}} → rewritten to the
|
|
59
|
+
// canonical URL form so the {{figma:URL}} rule below owns the rendering.
|
|
60
|
+
.replace(/\{\{figma:(?!https?:)([A-Za-z0-9]+)(?::([0-9]+[-:][0-9]+))?\}\}/g, (match, key, node) => {
|
|
61
|
+
const nodeParam = node ? `?node-id=${String(node).replace(':', '-')}` : '';
|
|
62
|
+
return `{{figma:https://www.figma.com/design/${key}${nodeParam}}}`;
|
|
63
|
+
})
|
|
58
64
|
// Figma embeds: {{figma:URL}}
|
|
59
65
|
.replace(/\{\{figma:([^}]+)\}\}/g, (match, url) => {
|
|
60
66
|
return `\n\n<div class="figma-embed" data-figma-url="${escapeAttr(url.trim())}"></div>\n\n`;
|
|
@@ -63,6 +69,11 @@ export const processShortcodes = (content: string): string => {
|
|
|
63
69
|
.replace(/\{\{linkedin:([^}]+)\}\}/g, (match, url) => {
|
|
64
70
|
return `\n\n<div class="linkedin-embed" data-post-url="${escapeAttr(url.trim())}"></div>\n\n`;
|
|
65
71
|
})
|
|
72
|
+
// Claude artifact / Claude Design: claude.ai serves `frame-ancestors 'self'` (cannot be
|
|
73
|
+
// framed by anyone), so both render as the OG link card — never an iframe.
|
|
74
|
+
.replace(/\{\{claude-(?:artifact|design):([^}]+)\}\}/g, (match, url) => {
|
|
75
|
+
return `\n\n<div class="link-preview" data-url="${escapeAttr(url.trim())}"></div>\n\n`;
|
|
76
|
+
})
|
|
66
77
|
// Link previews: {{link:URL}}
|
|
67
78
|
.replace(/\{\{link:([^}]+)\}\}/g, (match, url) => {
|
|
68
79
|
return `\n\n<div class="link-preview" data-url="${escapeAttr(url.trim())}"></div>\n\n`;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Departments — THE single source of truth for the org's department
|
|
3
|
+
* vocabulary.
|
|
4
|
+
*
|
|
5
|
+
* The `departments` table is populated from Google Workspace (the org chart)
|
|
6
|
+
* by the hub's Google sync, which auto-creates a row the first time it sees a
|
|
7
|
+
* new department name. `profiles.department_id` is an FK to it; there is no
|
|
8
|
+
* free-text department column and no enum anywhere in code — every consumer
|
|
9
|
+
* reads the row (or the embedded `DepartmentRef`) and every picker lists the
|
|
10
|
+
* ACTIVE rows ordered by `display_order`.
|
|
11
|
+
*
|
|
12
|
+
* Hub owner module: `lib/data/department-utils.ts`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface Department {
|
|
16
|
+
id: string
|
|
17
|
+
name: string
|
|
18
|
+
slug: string
|
|
19
|
+
description?: string | null
|
|
20
|
+
parent_department_id?: string | null
|
|
21
|
+
display_order: number
|
|
22
|
+
is_active: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The embedded shape a reader gets through the `department:departments(…)` PostgREST embed. */
|
|
26
|
+
export type DepartmentRef = Pick<Department, 'id' | 'name' | 'slug'> & {
|
|
27
|
+
display_order?: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** `GET /api/admin/departments?counts=…` row — a department plus how many profiles carry it. */
|
|
31
|
+
export interface DepartmentSummary {
|
|
32
|
+
id: string
|
|
33
|
+
name: string
|
|
34
|
+
slug: string
|
|
35
|
+
count: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Population a department count is taken over. */
|
|
39
|
+
export type DepartmentCountPopulation = 'directory' | 'scored'
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// Design Doc Types (product-hub)
|
|
2
|
+
// Mirrors product-release.ts — entity + child tables + create/update/filter shapes.
|
|
3
|
+
//
|
|
4
|
+
// Participant sections are keyed by the employee DEPARTMENT (FK to the ONE
|
|
5
|
+
// `departments` table — see department.ts); there is no function enum.
|
|
6
|
+
// Completion-gate math and status transitions live in the hub's
|
|
7
|
+
// `lib/utils/design-doc-gate.ts` (isomorphic, shared by server + client).
|
|
8
|
+
|
|
9
|
+
import type { EntityAuthor } from './entity-author'
|
|
10
|
+
import type { DeliveryItem } from './delivery'
|
|
11
|
+
import type { DepartmentRef } from './department'
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Value vocabularies (string unions + `as const` arrays; badge colors and
|
|
15
|
+
// option labels are declared ONCE in the hub gate module, not here)
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export const DESIGN_DOC_STATUSES = ['draft', 'in_review', 'approved', 'building', 'shipped', 'abandoned'] as const
|
|
19
|
+
export type DesignDocStatus = (typeof DESIGN_DOC_STATUSES)[number]
|
|
20
|
+
|
|
21
|
+
export const DESIGN_DOC_TIERS = ['S', 'M', 'L'] as const
|
|
22
|
+
export type DesignDocTier = (typeof DESIGN_DOC_TIERS)[number]
|
|
23
|
+
|
|
24
|
+
export const DESIGN_DOC_PARTICIPANT_STATUSES = ['pending', 'in_progress', 'completed', 'blocked'] as const
|
|
25
|
+
export type DesignDocParticipantStatus = (typeof DESIGN_DOC_PARTICIPANT_STATUSES)[number]
|
|
26
|
+
|
|
27
|
+
export const DESIGN_DOC_COMMENT_TYPES = ['blocking', 'suggestion', 'question', 'note'] as const
|
|
28
|
+
export type DesignDocCommentType = (typeof DESIGN_DOC_COMMENT_TYPES)[number]
|
|
29
|
+
|
|
30
|
+
export const DESIGN_DOC_COMMENT_STATUSES = ['open', 'resolved', 'declined'] as const
|
|
31
|
+
export type DesignDocCommentStatus = (typeof DESIGN_DOC_COMMENT_STATUSES)[number]
|
|
32
|
+
|
|
33
|
+
export const DESIGN_DOC_LINK_TYPES = ['roadmap_task', 'figma', 'claude_artifact', 'claude_design', 'prototype', 'brief', 'other'] as const
|
|
34
|
+
export type DesignDocLinkType = (typeof DESIGN_DOC_LINK_TYPES)[number]
|
|
35
|
+
|
|
36
|
+
export const DESIGN_DOC_MEDIA_TYPES = ['image', 'video', 'screenshot', 'demo'] as const
|
|
37
|
+
export type DesignDocMediaType = (typeof DESIGN_DOC_MEDIA_TYPES)[number]
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Rows (as returned by the hub DAL — hydrated)
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
/** Minimal profile identity the DAL embeds on participants / comment authors. */
|
|
44
|
+
export interface DesignDocPerson {
|
|
45
|
+
id: string
|
|
46
|
+
full_name: string | null
|
|
47
|
+
avatar_url: string | null
|
|
48
|
+
job_title?: string | null
|
|
49
|
+
/** Present on the DRI/detail payload only — never on list payloads. */
|
|
50
|
+
email?: string | null
|
|
51
|
+
department_id?: string | null
|
|
52
|
+
department?: DepartmentRef | null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface DesignDocClickUpTask {
|
|
56
|
+
id: number
|
|
57
|
+
doc_id: number
|
|
58
|
+
participant_id: number | null
|
|
59
|
+
clickup_task_id: string
|
|
60
|
+
display_order: number | null
|
|
61
|
+
created_at: string
|
|
62
|
+
/** Hydrated from the `clickup_tasks` mirror (never the live API). `undefined` = not in the mirror. */
|
|
63
|
+
task?: DeliveryItem
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface DesignDocLink {
|
|
67
|
+
id: number
|
|
68
|
+
doc_id: number
|
|
69
|
+
link_type: DesignDocLinkType
|
|
70
|
+
url: string | null
|
|
71
|
+
clickup_task_id: string | null
|
|
72
|
+
title: string | null
|
|
73
|
+
display_order: number | null
|
|
74
|
+
created_at: string
|
|
75
|
+
/** `roadmap_task` links are hydrated from the mirror too. */
|
|
76
|
+
task?: DeliveryItem
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface DesignDocMedia {
|
|
80
|
+
id: number
|
|
81
|
+
doc_id: number
|
|
82
|
+
participant_id: number | null
|
|
83
|
+
media_type: DesignDocMediaType
|
|
84
|
+
media_url: string
|
|
85
|
+
title: string | null
|
|
86
|
+
description: string | null
|
|
87
|
+
display_order: number | null
|
|
88
|
+
created_at: string
|
|
89
|
+
created_by: string | null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A comment. ROOT comments (`parent_comment_id === null`) carry `comment_type`
|
|
94
|
+
* + `status` and gate approval when `blocking` + `open`; REPLIES carry neither
|
|
95
|
+
* (single-level threads — a reply to a reply is re-parented to the root).
|
|
96
|
+
*/
|
|
97
|
+
export interface DesignDocComment {
|
|
98
|
+
id: number
|
|
99
|
+
doc_id: number
|
|
100
|
+
participant_id: number | null
|
|
101
|
+
parent_comment_id: number | null
|
|
102
|
+
author_id: string | null
|
|
103
|
+
comment_type: DesignDocCommentType | null
|
|
104
|
+
title: string | null
|
|
105
|
+
body: string
|
|
106
|
+
status: DesignDocCommentStatus | null
|
|
107
|
+
resolved_at: string | null
|
|
108
|
+
created_at: string
|
|
109
|
+
author?: DesignDocPerson | null
|
|
110
|
+
/** Threaded by the DAL; always `[]` on replies. */
|
|
111
|
+
replies: DesignDocComment[]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface DesignDocParticipant {
|
|
115
|
+
id: number
|
|
116
|
+
doc_id: number
|
|
117
|
+
department_id: string
|
|
118
|
+
department: DepartmentRef
|
|
119
|
+
assignee_id: string | null
|
|
120
|
+
assignee?: DesignDocPerson | null
|
|
121
|
+
status: DesignDocParticipantStatus
|
|
122
|
+
content: string | null
|
|
123
|
+
completed_at: string | null
|
|
124
|
+
display_order: number | null
|
|
125
|
+
created_at: string
|
|
126
|
+
updated_at: string
|
|
127
|
+
tasks: DesignDocClickUpTask[]
|
|
128
|
+
media: DesignDocMedia[]
|
|
129
|
+
/** Section-scoped root comments (threaded). */
|
|
130
|
+
comments: DesignDocComment[]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface DesignDocCompletion {
|
|
134
|
+
total: number
|
|
135
|
+
completed: number
|
|
136
|
+
blocked: number
|
|
137
|
+
openBlocking: number
|
|
138
|
+
isComplete: boolean
|
|
139
|
+
waitingOn: Array<{
|
|
140
|
+
participantId: number
|
|
141
|
+
department: DepartmentRef
|
|
142
|
+
assigneeName: string | null
|
|
143
|
+
status: DesignDocParticipantStatus
|
|
144
|
+
}>
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** R13 — what the signed-in viewer still owes on one doc. */
|
|
148
|
+
export interface DesignDocMyOpenWork {
|
|
149
|
+
/** One person may own several department sections on the same doc. */
|
|
150
|
+
participants: Array<{ id: number; department: DepartmentRef; status: DesignDocParticipantStatus }>
|
|
151
|
+
openTasks: DeliveryItem[]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface DesignDoc {
|
|
155
|
+
id: number
|
|
156
|
+
title: string
|
|
157
|
+
slug: string
|
|
158
|
+
tier: DesignDocTier
|
|
159
|
+
summary: string | null
|
|
160
|
+
content: string | null
|
|
161
|
+
status: DesignDocStatus
|
|
162
|
+
author_id: string | null
|
|
163
|
+
approved_at: string | null
|
|
164
|
+
created_at: string
|
|
165
|
+
updated_at: string
|
|
166
|
+
/** DRI, hydrated via the shared author hydrator. */
|
|
167
|
+
author?: EntityAuthor
|
|
168
|
+
participants: DesignDocParticipant[]
|
|
169
|
+
links: DesignDocLink[]
|
|
170
|
+
/** Doc-level comments only (participant-scoped comments ride on their participant). */
|
|
171
|
+
comments: DesignDocComment[]
|
|
172
|
+
completion: DesignDocCompletion
|
|
173
|
+
my_open_work?: DesignDocMyOpenWork
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// Write shapes
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
export interface DesignDocLinkInput {
|
|
181
|
+
link_type: DesignDocLinkType
|
|
182
|
+
url?: string | null
|
|
183
|
+
clickup_task_id?: string | null
|
|
184
|
+
title?: string | null
|
|
185
|
+
display_order?: number
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface DesignDocClickUpTaskInput {
|
|
189
|
+
clickup_task_id: string
|
|
190
|
+
display_order?: number
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface DesignDocMediaInput {
|
|
194
|
+
media_type: DesignDocMediaType
|
|
195
|
+
media_url: string
|
|
196
|
+
title?: string | null
|
|
197
|
+
description?: string | null
|
|
198
|
+
display_order?: number
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface CreateDesignDocParticipantInput {
|
|
202
|
+
/** Defaults to the assignee's profile department when omitted. */
|
|
203
|
+
department_id?: string
|
|
204
|
+
assignee_id: string
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Wizard payload (POST /api/admin/design-docs). */
|
|
208
|
+
export interface CreateDesignDocData {
|
|
209
|
+
title: string
|
|
210
|
+
slug: string
|
|
211
|
+
tier: DesignDocTier
|
|
212
|
+
summary?: string | null
|
|
213
|
+
/** DRI; defaults to the caller. */
|
|
214
|
+
author_id?: string | null
|
|
215
|
+
participants: CreateDesignDocParticipantInput[]
|
|
216
|
+
links: DesignDocLinkInput[]
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Doc-level allowlist (PUT). `status` and `participants` are NOT here — status
|
|
221
|
+
* only moves through PATCH (the gate), participants only through their routes.
|
|
222
|
+
*/
|
|
223
|
+
export interface UpdateDesignDocData {
|
|
224
|
+
title?: string
|
|
225
|
+
slug?: string
|
|
226
|
+
tier?: DesignDocTier
|
|
227
|
+
summary?: string | null
|
|
228
|
+
content?: string | null
|
|
229
|
+
author_id?: string | null
|
|
230
|
+
links?: DesignDocLinkInput[]
|
|
231
|
+
/** OCC token — the `updated_at` the editor seeded from; mismatch → 409. */
|
|
232
|
+
expected_updated_at?: string
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface UpdateDesignDocParticipantData {
|
|
236
|
+
content?: string | null
|
|
237
|
+
status?: DesignDocParticipantStatus
|
|
238
|
+
assignee_id?: string | null
|
|
239
|
+
department_id?: string
|
|
240
|
+
clickup_tasks?: DesignDocClickUpTaskInput[]
|
|
241
|
+
media?: DesignDocMediaInput[]
|
|
242
|
+
expected_updated_at?: string
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface AddDesignDocParticipantData {
|
|
246
|
+
/** Defaults to the assignee's profile department when omitted. */
|
|
247
|
+
department_id?: string
|
|
248
|
+
assignee_id: string
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface AddDesignDocCommentData {
|
|
252
|
+
participant_id?: number | null
|
|
253
|
+
/** Set for a reply; the DAL re-parents replies-of-replies to the root. */
|
|
254
|
+
parent_comment_id?: number | null
|
|
255
|
+
/** Required on root comments, ignored on replies. */
|
|
256
|
+
comment_type?: DesignDocCommentType
|
|
257
|
+
title?: string | null
|
|
258
|
+
body: string
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export interface DesignDocFilters {
|
|
262
|
+
status?: string
|
|
263
|
+
search?: string
|
|
264
|
+
mine?: 'open'
|
|
265
|
+
limit?: number
|
|
266
|
+
offset?: number
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface DesignDocListResponse {
|
|
270
|
+
data: DesignDoc[]
|
|
271
|
+
count: number
|
|
272
|
+
/** Server-computed status facet counts (how-i-work feed convention). */
|
|
273
|
+
facets: { status: Record<string, number> }
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export interface DesignDocStats {
|
|
277
|
+
total: number
|
|
278
|
+
draft: number
|
|
279
|
+
in_review: number
|
|
280
|
+
approved: number
|
|
281
|
+
building: number
|
|
282
|
+
waiting_on_me_docs: number
|
|
283
|
+
waiting_on_me_tasks: number
|
|
284
|
+
}
|
package/src/types/employee.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import type { DepartmentRef } from './department';
|
|
2
|
+
|
|
1
3
|
export interface Employee {
|
|
2
4
|
id: string;
|
|
3
5
|
full_name: string;
|
|
4
|
-
department
|
|
6
|
+
/** FK to the ONE `departments` table; `department` is the embedded row. */
|
|
7
|
+
department_id: string | null;
|
|
8
|
+
department: DepartmentRef | null;
|
|
5
9
|
avatar_url: string;
|
|
6
10
|
role: string;
|
|
7
11
|
job_title: string;
|
|
@@ -16,7 +20,8 @@ export interface EmployeeResponse {
|
|
|
16
20
|
|
|
17
21
|
export interface EmployeeProfileData {
|
|
18
22
|
full_name: string;
|
|
19
|
-
|
|
23
|
+
department_id: string | null;
|
|
24
|
+
department: DepartmentRef | null;
|
|
20
25
|
avatar_url: string;
|
|
21
26
|
role: string;
|
|
22
27
|
job_title: string;
|
package/src/types/index.ts
CHANGED
|
@@ -28,11 +28,13 @@ export * from './customer-interview-ai.types'
|
|
|
28
28
|
export * from './luma'
|
|
29
29
|
export * from './product-release'
|
|
30
30
|
export * from './delivery'
|
|
31
|
+
export * from './design-doc'
|
|
31
32
|
export * from './vendor'
|
|
32
33
|
export * from './vendor-links'
|
|
33
34
|
export * from './video-processing'
|
|
34
35
|
|
|
35
36
|
// User and profile types
|
|
37
|
+
export * from './department'
|
|
36
38
|
export * from './employee'
|
|
37
39
|
export * from './profile'
|
|
38
40
|
export * from './team'
|
package/src/types/marketing.ts
CHANGED
|
@@ -350,6 +350,7 @@ export type ContentSourceType =
|
|
|
350
350
|
| 'what_i_shipped' // What I Shipped employee check-ins (lives on people-hub)
|
|
351
351
|
| 'how_i_work' // How I Work employee AI-workflow sessions (lives on people-hub)
|
|
352
352
|
| 'faq' // FAQ Q&A pair (single-page /faqs index; deep-link by category anchor)
|
|
353
|
+
| 'design_doc' // Internal design docs (product-hub admin; preview route only, no public page)
|
|
353
354
|
| 'from_scratch';
|
|
354
355
|
|
|
355
356
|
export type URLInjectionPreference = 'none' | 'in_post' | 'as_comment';
|
package/src/types/profile.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Profile system TypeScript interfaces for OpenMSP
|
|
2
2
|
|
|
3
|
+
import type { DepartmentRef } from './department'
|
|
4
|
+
|
|
3
5
|
export interface ProfileResponse {
|
|
4
6
|
id: string;
|
|
5
7
|
name: string;
|
|
@@ -14,7 +16,7 @@ export interface ProfileResponse {
|
|
|
14
16
|
jobTitle: string | null;
|
|
15
17
|
isCommunityMember: boolean;
|
|
16
18
|
lastSignIn: string | null;
|
|
17
|
-
department:
|
|
19
|
+
department: DepartmentRef | null;
|
|
18
20
|
employeeId: string | null;
|
|
19
21
|
}
|
|
20
22
|
|
package/src/types/team.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { DepartmentRef } from './department';
|
|
2
|
+
|
|
1
3
|
export interface TeamMember {
|
|
2
4
|
id: string;
|
|
3
5
|
employee_id: string;
|
|
@@ -9,7 +11,9 @@ export interface TeamMember {
|
|
|
9
11
|
phone?: string;
|
|
10
12
|
role?: string;
|
|
11
13
|
title?: string;
|
|
14
|
+
/** Display name of the department (presentational shape); `department_id` is the FK. */
|
|
12
15
|
department?: string;
|
|
16
|
+
department_id?: string | null;
|
|
13
17
|
team?: string;
|
|
14
18
|
manager_id?: string;
|
|
15
19
|
profile_image_url?: string;
|
|
@@ -33,7 +37,11 @@ export interface TeamMember {
|
|
|
33
37
|
|
|
34
38
|
export interface TeamSection {
|
|
35
39
|
title: string;
|
|
40
|
+
/** Department display name. */
|
|
36
41
|
department: string;
|
|
42
|
+
department_id: string | null;
|
|
43
|
+
/** `departments.display_order` — sections sort by it (no name sentinels). */
|
|
44
|
+
display_order: number;
|
|
37
45
|
description: string;
|
|
38
46
|
members: TeamMember[];
|
|
39
47
|
}
|
|
@@ -45,5 +53,5 @@ export interface TeamData {
|
|
|
45
53
|
|
|
46
54
|
export interface GetTeamOptions {
|
|
47
55
|
includeInactive?: boolean;
|
|
48
|
-
|
|
56
|
+
departmentId?: string;
|
|
49
57
|
}
|