@burdenoff/microfe-timecampus 2026.626.2 → 2026.703.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/generated/wspace-operations.js +996 -816
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/timecampus/TimeCampusRoutes.js +16 -12
- package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +81 -47
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/timeline/TimelinePage.js +250 -0
- package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -0
- package/dist/timecampus/utils/helpers.js +20 -10
- package/dist/timecampus/utils/helpers.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientPortalPage.js","names":[],"sources":["../../../../src/timecampus/pages/client-portal/ClientPortalPage.tsx"],"sourcesContent":["import { Building2, Plus, DollarSign, FolderOpen } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n getStatusColor,\n formatCurrency,\n getInitials,\n} from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListClientPortalConfigsQuery,\n useGetClientPortalStatsQuery,\n type ClientPortalFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function ClientPortalPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: configsData,\n loading: configsLoading,\n error: configsError,\n } = useListClientPortalConfigsQuery();\n const { data: statsData, loading: statsLoading } =\n useGetClientPortalStatsQuery();\n\n const clients = configsData?.clientPortalConfigs?.items ?? [];\n const stats = statsData?.clientPortalStats;\n\n if (configsLoading) {\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (configsError) {\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load client portal data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\n purpose=\"Give clients a transparent window into the work you do for them — the projects, hours and progress tied to each one — so billing conversations are backed by data and trust, not spreadsheets.\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Client\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Active Clients\"\n value={String(stats?.activeClients ?? 0)}\n icon={<Building2 className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Projects\"\n value={String(stats?.totalProjectsShared ?? 0)}\n icon={<FolderOpen className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Clients\"\n value={String(stats?.totalClients ?? 0)}\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Active\"\n value={String(stats?.activeClients ?? 0)}\n trend={{ value: 0, label: \"clients\" }}\n />\n </div>\n\n {clients.length === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No clients configured yet\"\n description=\"Add your first client to share projects and tracked hours with them.\"\n action={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Client\n </button>\n }\n />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {clients.map((client: ClientPortalFieldsFragment) => {\n const totalTrackedHours = client.projects.reduce(\n (sum: number, p: { trackedHours: number }) =>\n sum + p.trackedHours,\n 0,\n );\n return (\n <div\n key={client.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-sm font-medium text-primary\">\n {getInitials(client.clientName)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {client.clientName}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {client.projects.length} projects ·{\" \"}\n {totalTrackedHours}h tracked\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n {client.contactEmail && (\n <span className=\"text-xs text-text-secondary\">\n {client.contactEmail}\n </span>\n )}\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(client.isActive ? \"active\" : \"inactive\")}`}\n >\n {client.isActive ? \"active\" : \"inactive\"}\n </span>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,IAAmB;CACjC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,GAAiC,EAC/B,EAAE,MAAM,GAAW,SAAS,MAChC,GAA8B,EAE1B,IAAU,GAAa,qBAAqB,SAAS,EAAE,EACvD,IAAQ,GAAW;AA0BzB,QAxBI,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,aAErB;;YARb,CAWE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,UAAA;KACA,OAAM;KACN,OAAO,OAAO,GAAO,iBAAiB,EAAE;KACxC,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;KACvC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,uBAAuB,EAAE;KAC9C,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;KACxC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,gBAAgB,EAAE;KACvC,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;KACxC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,iBAAiB,EAAE;KACxC,OAAO;MAAE,OAAO;MAAG,OAAO;MAAW;KACrC,CAAA;IACE;MAEL,EAAQ,WAAW,IAClB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,aAErB;;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,KAAK,MAAuC;KACnD,IAAM,IAAoB,EAAO,SAAS,QACvC,GAAa,MACZ,IAAM,EAAE,cACV,EACD;AACD,YACE,kBAAC,OAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAY,EAAO,WAAW;QAC3B,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAO;QACN,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAO,SAAS;SAAO;SAAmB;SAC1C;SAAkB;SACjB;UACA,EAAA,CAAA,CACF;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,EAAO,gBACN,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAO;QACH,CAAA,EAET,kBAAC,QAAD;QACE,WAAW,4DAA4D,EAAe,EAAO,WAAW,WAAW,WAAW;kBAE7H,EAAO,WAAW,WAAW;QACzB,CAAA,CACH;SACF;QA7BC,EAAO,GA6BR;MAER;IACE,CAAA;GACF,CAAA,CAEG"}
|
|
1
|
+
{"version":3,"file":"ClientPortalPage.js","names":[],"sources":["../../../../src/timecampus/pages/client-portal/ClientPortalPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport {\n Building2,\n Plus,\n DollarSign,\n FolderOpen,\n Link2,\n Check,\n RefreshCw,\n} from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { getStatusColor, getInitials } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListClientPortalConfigsQuery,\n useGetClientPortalStatsQuery,\n useRegenerateClientPortalShareTokenMutation,\n type ClientPortalFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\n/**\n * Build the public share URL for a portal token. The public client view is\n * served by the website at /portal/:token; only active portals resolve.\n */\nfunction shareUrlFor(token: string): string {\n const origin =\n typeof window !== \"undefined\" ? window.location.origin : \"\";\n return `${origin}/portal/${token}`;\n}\n\nexport function ClientPortalPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const [copiedId, setCopiedId] = useState<string | null>(null);\n\n const {\n data: configsData,\n loading: configsLoading,\n error: configsError,\n refetch,\n } = useListClientPortalConfigsQuery();\n const { data: statsData } = useGetClientPortalStatsQuery();\n const [regenerate, { loading: regenerating }] =\n useRegenerateClientPortalShareTokenMutation();\n\n const clients = configsData?.clientPortalConfigs?.items ?? [];\n const stats = statsData?.clientPortalStats;\n\n const handleCopy = async (client: ClientPortalFieldsFragment) => {\n if (!client.shareToken) return;\n try {\n await navigator.clipboard.writeText(shareUrlFor(client.shareToken));\n setCopiedId(client.id);\n window.setTimeout(() => setCopiedId(null), 2000);\n } catch {\n // Clipboard unavailable — silently ignore.\n }\n };\n\n const handleRegenerate = async (client: ClientPortalFieldsFragment) => {\n await regenerate({ variables: { id: client.id } });\n await refetch();\n };\n\n if (configsLoading) {\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (configsError) {\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load client portal data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\n purpose=\"Give clients a transparent window into the work you do for them — the projects, hours and progress tied to each one — so billing conversations are backed by data and trust, not spreadsheets.\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Client\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Active Clients\"\n value={String(stats?.activeClients ?? 0)}\n icon={<Building2 className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Projects\"\n value={String(stats?.totalProjectsShared ?? 0)}\n icon={<FolderOpen className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Clients\"\n value={String(stats?.totalClients ?? 0)}\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Active\"\n value={String(stats?.activeClients ?? 0)}\n trend={{ value: 0, label: \"clients\" }}\n />\n </div>\n\n {clients.length === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No clients configured yet\"\n description=\"Add your first client to share projects and tracked hours with them.\"\n action={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Client\n </button>\n }\n />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {clients.map((client: ClientPortalFieldsFragment) => {\n const totalTrackedHours = client.projects.reduce(\n (sum: number, p: { trackedHours: number }) =>\n sum + p.trackedHours,\n 0,\n );\n return (\n <div\n key={client.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-sm font-medium text-primary\">\n {getInitials(client.clientName)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {client.clientName}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {client.projects.length} projects ·{\" \"}\n {totalTrackedHours}h tracked\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n {client.contactEmail && (\n <span className=\"hidden text-xs text-text-secondary sm:inline\">\n {client.contactEmail}\n </span>\n )}\n {client.isActive && client.shareToken && (\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n void handleCopy(client);\n }}\n title=\"Copy the client's private share link\"\n className=\"inline-flex items-center gap-1 rounded-md border border-border-seam bg-bg-surface px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken/50\"\n >\n {copiedId === client.id ? (\n <>\n <Check className=\"h-3.5 w-3.5\" /> Copied\n </>\n ) : (\n <>\n <Link2 className=\"h-3.5 w-3.5\" /> Share link\n </>\n )}\n </button>\n <button\n type=\"button\"\n disabled={regenerating}\n onClick={(e) => {\n e.stopPropagation();\n void handleRegenerate(client);\n }}\n title=\"Regenerate the link (invalidates the old one)\"\n className=\"inline-flex items-center rounded-md border border-border-seam bg-bg-surface p-1 text-text-secondary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n <RefreshCw className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n )}\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(client.isActive ? \"active\" : \"inactive\")}`}\n >\n {client.isActive ? \"active\" : \"inactive\"}\n </span>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
+
import { formatDuration as t, formatTime as n } from "../../utils/helpers.js";
|
|
3
|
+
import { StatCard as r } from "../../components/StatCard.js";
|
|
4
|
+
import { LoadingSkeleton as i } from "../../components/LoadingSkeleton.js";
|
|
5
|
+
import { useGetTimeCampusTimelineQuery as a, useRecategorizeTimeCampusCaptureEventMutation as o } from "../../../generated/wspace-operations.js";
|
|
6
|
+
import { PageLayout as s } from "../../components/PageLayout.js";
|
|
7
|
+
import { EmptyState as c } from "../../components/EmptyState.js";
|
|
8
|
+
import { useMemo as l, useState as u } from "react";
|
|
9
|
+
import { Activity as d, Brain as f, Calendar as p, Coffee as m, Github as h, MessageSquare as g, Users as _, Zap as v } from "lucide-react";
|
|
10
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
11
|
+
import { GlassCard as x } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
//#region src/timecampus/pages/timeline/TimelinePage.tsx
|
|
13
|
+
var S = {
|
|
14
|
+
DEEP_WORK: {
|
|
15
|
+
label: "Deep work",
|
|
16
|
+
icon: f,
|
|
17
|
+
dot: "bg-status-success-bg",
|
|
18
|
+
chip: "bg-status-success-bg text-status-success-text"
|
|
19
|
+
},
|
|
20
|
+
MEETING: {
|
|
21
|
+
label: "Meeting",
|
|
22
|
+
icon: _,
|
|
23
|
+
dot: "bg-status-info-bg",
|
|
24
|
+
chip: "bg-status-info-bg text-status-info-text"
|
|
25
|
+
},
|
|
26
|
+
COMMUNICATION: {
|
|
27
|
+
label: "Communication",
|
|
28
|
+
icon: g,
|
|
29
|
+
dot: "bg-status-warning-bg",
|
|
30
|
+
chip: "bg-status-warning-bg text-status-warning-text"
|
|
31
|
+
},
|
|
32
|
+
BREAK: {
|
|
33
|
+
label: "Break",
|
|
34
|
+
icon: m,
|
|
35
|
+
dot: "bg-bg-sunken",
|
|
36
|
+
chip: "bg-bg-sunken text-text-secondary"
|
|
37
|
+
},
|
|
38
|
+
OTHER: {
|
|
39
|
+
label: "Other",
|
|
40
|
+
icon: d,
|
|
41
|
+
dot: "bg-bg-sunken",
|
|
42
|
+
chip: "bg-bg-sunken text-text-secondary"
|
|
43
|
+
}
|
|
44
|
+
}, C = {
|
|
45
|
+
TIME_ENTRY: v,
|
|
46
|
+
MEETING: _,
|
|
47
|
+
POMODORO: f,
|
|
48
|
+
EMAIL: g,
|
|
49
|
+
CAPTURE: h
|
|
50
|
+
}, w = [
|
|
51
|
+
"DEEP_WORK",
|
|
52
|
+
"MEETING",
|
|
53
|
+
"COMMUNICATION",
|
|
54
|
+
"BREAK",
|
|
55
|
+
"OTHER"
|
|
56
|
+
];
|
|
57
|
+
function T(e) {
|
|
58
|
+
let t = e.getTimezoneOffset();
|
|
59
|
+
return (/* @__PURE__ */ new Date(e.getTime() - t * 60 * 1e3)).toISOString().slice(0, 10);
|
|
60
|
+
}
|
|
61
|
+
function E(e) {
|
|
62
|
+
let t = /* @__PURE__ */ new Date(`${e}T00:00:00`), n = /* @__PURE__ */ new Date(`${e}T23:59:59.999`);
|
|
63
|
+
return {
|
|
64
|
+
from: t.toISOString(),
|
|
65
|
+
to: n.toISOString()
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function D() {
|
|
69
|
+
let { workspaceId: m = "" } = e(), [h, g] = u(() => T(/* @__PURE__ */ new Date())), { from: _, to: D } = l(() => E(h), [h]), { data: O, loading: k, error: A, refetch: j } = a({
|
|
70
|
+
variables: {
|
|
71
|
+
from: _,
|
|
72
|
+
to: D
|
|
73
|
+
},
|
|
74
|
+
skip: !m,
|
|
75
|
+
fetchPolicy: "cache-and-network"
|
|
76
|
+
}), [M, { loading: N }] = o(), P = O?.timeCampusTimeline, F = P?.segments ?? [], I = P?.summary, L = l(() => {
|
|
77
|
+
let e = /* @__PURE__ */ new Map();
|
|
78
|
+
for (let t of I?.byCategory ?? []) e.set(t.category, {
|
|
79
|
+
totalSeconds: t.totalSeconds,
|
|
80
|
+
segmentCount: t.segmentCount
|
|
81
|
+
});
|
|
82
|
+
return e;
|
|
83
|
+
}, [I]), R = async (e, t) => {
|
|
84
|
+
!e.isRecategorizable || e.category === t || (await M({ variables: {
|
|
85
|
+
id: e.sourceId,
|
|
86
|
+
category: t
|
|
87
|
+
} }), await j());
|
|
88
|
+
}, z = I && I.focusRatio != null ? Math.round(I.focusRatio * 100) : 0;
|
|
89
|
+
return /* @__PURE__ */ y(s, {
|
|
90
|
+
title: "Timeline",
|
|
91
|
+
description: "Your day, assembled automatically",
|
|
92
|
+
purpose: "See where your time actually went — meetings, deep work, communication and breaks stitched into one honest, chronological view from everything TimeCampus captures, so you can spot the patterns worth changing.",
|
|
93
|
+
actions: /* @__PURE__ */ b("label", {
|
|
94
|
+
className: "flex items-center gap-2 text-sm text-text-secondary",
|
|
95
|
+
children: [/* @__PURE__ */ y(p, { className: "h-4 w-4" }), /* @__PURE__ */ y("input", {
|
|
96
|
+
type: "date",
|
|
97
|
+
value: h,
|
|
98
|
+
max: T(/* @__PURE__ */ new Date()),
|
|
99
|
+
onChange: (e) => g(e.target.value),
|
|
100
|
+
className: "rounded-md border border-border-seam bg-bg-surface px-3 py-1.5 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40"
|
|
101
|
+
})]
|
|
102
|
+
}),
|
|
103
|
+
children: k && !P ? /* @__PURE__ */ b("div", {
|
|
104
|
+
className: "space-y-6",
|
|
105
|
+
children: [/* @__PURE__ */ y(i, {
|
|
106
|
+
variant: "card",
|
|
107
|
+
count: 4
|
|
108
|
+
}), /* @__PURE__ */ y(i, {
|
|
109
|
+
variant: "list",
|
|
110
|
+
count: 6
|
|
111
|
+
})]
|
|
112
|
+
}) : A ? /* @__PURE__ */ y("div", {
|
|
113
|
+
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
114
|
+
children: "Failed to load your timeline. Please try again later."
|
|
115
|
+
}) : /* @__PURE__ */ b("div", {
|
|
116
|
+
className: "space-y-6",
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ b("div", {
|
|
119
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
120
|
+
children: [
|
|
121
|
+
/* @__PURE__ */ y(r, {
|
|
122
|
+
featured: !0,
|
|
123
|
+
title: "Tracked time",
|
|
124
|
+
value: t(I?.totalTrackedSeconds ?? 0),
|
|
125
|
+
icon: /* @__PURE__ */ y(d, { className: "h-5 w-5" }),
|
|
126
|
+
description: "Total activity across the day"
|
|
127
|
+
}),
|
|
128
|
+
/* @__PURE__ */ y(r, {
|
|
129
|
+
title: "Focus ratio",
|
|
130
|
+
value: `${z}%`,
|
|
131
|
+
icon: /* @__PURE__ */ y(f, { className: "h-5 w-5" }),
|
|
132
|
+
description: "Share of time in deep work"
|
|
133
|
+
}),
|
|
134
|
+
/* @__PURE__ */ y(r, {
|
|
135
|
+
title: "Context switches",
|
|
136
|
+
value: I?.contextSwitches ?? 0,
|
|
137
|
+
icon: /* @__PURE__ */ y(v, { className: "h-5 w-5" }),
|
|
138
|
+
description: "Category changes through the day"
|
|
139
|
+
}),
|
|
140
|
+
/* @__PURE__ */ y(r, {
|
|
141
|
+
title: "Longest focus block",
|
|
142
|
+
value: t(I?.longestFocusBlockSeconds ?? 0),
|
|
143
|
+
icon: /* @__PURE__ */ y(f, { className: "h-5 w-5" }),
|
|
144
|
+
description: "Your deepest uninterrupted stretch"
|
|
145
|
+
})
|
|
146
|
+
]
|
|
147
|
+
}),
|
|
148
|
+
F.length > 0 && /* @__PURE__ */ b(x, {
|
|
149
|
+
treatment: "glass",
|
|
150
|
+
className: "p-4 sm:p-6",
|
|
151
|
+
children: [/* @__PURE__ */ y("h2", {
|
|
152
|
+
className: "mb-4 text-sm font-semibold text-text-primary",
|
|
153
|
+
children: "Where the time went"
|
|
154
|
+
}), /* @__PURE__ */ y("div", {
|
|
155
|
+
className: "flex flex-wrap gap-4",
|
|
156
|
+
children: w.filter((e) => L.has(e)).map((e) => {
|
|
157
|
+
let n = S[e], r = L.get(e);
|
|
158
|
+
return /* @__PURE__ */ b("div", {
|
|
159
|
+
className: "flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-2",
|
|
160
|
+
children: [
|
|
161
|
+
/* @__PURE__ */ y("span", {
|
|
162
|
+
className: `inline-block h-2.5 w-2.5 rounded-full ${n.dot}`,
|
|
163
|
+
"aria-hidden": !0
|
|
164
|
+
}),
|
|
165
|
+
/* @__PURE__ */ y("span", {
|
|
166
|
+
className: "text-sm font-medium text-text-primary",
|
|
167
|
+
children: n.label
|
|
168
|
+
}),
|
|
169
|
+
/* @__PURE__ */ y("span", {
|
|
170
|
+
className: "text-sm tabular-nums text-text-secondary",
|
|
171
|
+
children: t(r.totalSeconds)
|
|
172
|
+
})
|
|
173
|
+
]
|
|
174
|
+
}, e);
|
|
175
|
+
})
|
|
176
|
+
})]
|
|
177
|
+
}),
|
|
178
|
+
F.length === 0 ? /* @__PURE__ */ y(c, {
|
|
179
|
+
illustration: "empty-generic",
|
|
180
|
+
title: "No activity captured for this day",
|
|
181
|
+
description: "As you track time, hold meetings, run focus sessions, and connect capture channels, your day assembles here automatically — categorized and in order."
|
|
182
|
+
}) : /* @__PURE__ */ y(x, {
|
|
183
|
+
treatment: "glass",
|
|
184
|
+
glow: !0,
|
|
185
|
+
className: "overflow-hidden",
|
|
186
|
+
children: /* @__PURE__ */ y("ol", {
|
|
187
|
+
className: "divide-y divide-border-seam",
|
|
188
|
+
children: F.map((e) => {
|
|
189
|
+
let r = e.category ?? "OTHER", i = S[r] ?? S.OTHER, a = C[e.source] ?? d;
|
|
190
|
+
return /* @__PURE__ */ b("li", {
|
|
191
|
+
className: "flex items-start gap-3 p-4 transition-colors hover:bg-bg-sunken/50",
|
|
192
|
+
children: [
|
|
193
|
+
/* @__PURE__ */ b("div", {
|
|
194
|
+
className: "flex w-16 shrink-0 flex-col items-end pt-0.5 text-xs tabular-nums text-text-secondary",
|
|
195
|
+
children: [/* @__PURE__ */ y("span", { children: n(e.startTime) }), /* @__PURE__ */ y("span", {
|
|
196
|
+
className: "text-text-tertiary",
|
|
197
|
+
children: t(e.durationSeconds)
|
|
198
|
+
})]
|
|
199
|
+
}),
|
|
200
|
+
/* @__PURE__ */ y("div", {
|
|
201
|
+
className: `mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${i.chip}`,
|
|
202
|
+
children: /* @__PURE__ */ y(a, { className: "h-4 w-4" })
|
|
203
|
+
}),
|
|
204
|
+
/* @__PURE__ */ b("div", {
|
|
205
|
+
className: "min-w-0 flex-1",
|
|
206
|
+
children: [/* @__PURE__ */ y("p", {
|
|
207
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
208
|
+
children: e.title || i.label
|
|
209
|
+
}), /* @__PURE__ */ b("div", {
|
|
210
|
+
className: "mt-1 flex flex-wrap items-center gap-2",
|
|
211
|
+
children: [
|
|
212
|
+
/* @__PURE__ */ y("span", {
|
|
213
|
+
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${i.chip}`,
|
|
214
|
+
children: i.label
|
|
215
|
+
}),
|
|
216
|
+
e.app && /* @__PURE__ */ y("span", {
|
|
217
|
+
className: "text-xs text-text-secondary",
|
|
218
|
+
children: e.app
|
|
219
|
+
}),
|
|
220
|
+
e.isRecategorizable && /* @__PURE__ */ y("span", {
|
|
221
|
+
className: "text-xs text-text-tertiary",
|
|
222
|
+
children: "via capture"
|
|
223
|
+
})
|
|
224
|
+
]
|
|
225
|
+
})]
|
|
226
|
+
}),
|
|
227
|
+
e.isRecategorizable && /* @__PURE__ */ y("select", {
|
|
228
|
+
"aria-label": "Recategorize activity",
|
|
229
|
+
disabled: N,
|
|
230
|
+
value: r,
|
|
231
|
+
onChange: (t) => R(e, t.target.value),
|
|
232
|
+
className: "shrink-0 rounded-md border border-border-seam bg-bg-surface px-2 py-1 text-xs text-text-secondary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40",
|
|
233
|
+
children: w.map((e) => /* @__PURE__ */ y("option", {
|
|
234
|
+
value: e,
|
|
235
|
+
children: S[e].label
|
|
236
|
+
}, e))
|
|
237
|
+
})
|
|
238
|
+
]
|
|
239
|
+
}, e.id);
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
]
|
|
244
|
+
})
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
export { D as TimelinePage };
|
|
249
|
+
|
|
250
|
+
//# sourceMappingURL=TimelinePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimelinePage.js","names":[],"sources":["../../../../src/timecampus/pages/timeline/TimelinePage.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport {\n Activity,\n Brain,\n Calendar,\n Coffee,\n Github,\n MessageSquare,\n Users,\n Zap,\n} from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatDuration, formatTime } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useGetTimeCampusTimelineQuery,\n useRecategorizeTimeCampusCaptureEventMutation,\n type TimelineSegmentFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\ntype Category = \"MEETING\" | \"DEEP_WORK\" | \"COMMUNICATION\" | \"BREAK\" | \"OTHER\";\n\nconst CATEGORY_META: Record<\n Category,\n { label: string; icon: typeof Brain; dot: string; chip: string }\n> = {\n DEEP_WORK: {\n label: \"Deep work\",\n icon: Brain,\n dot: \"bg-status-success-bg\",\n chip: \"bg-status-success-bg text-status-success-text\",\n },\n MEETING: {\n label: \"Meeting\",\n icon: Users,\n dot: \"bg-status-info-bg\",\n chip: \"bg-status-info-bg text-status-info-text\",\n },\n COMMUNICATION: {\n label: \"Communication\",\n icon: MessageSquare,\n dot: \"bg-status-warning-bg\",\n chip: \"bg-status-warning-bg text-status-warning-text\",\n },\n BREAK: {\n label: \"Break\",\n icon: Coffee,\n dot: \"bg-bg-sunken\",\n chip: \"bg-bg-sunken text-text-secondary\",\n },\n OTHER: {\n label: \"Other\",\n icon: Activity,\n dot: \"bg-bg-sunken\",\n chip: \"bg-bg-sunken text-text-secondary\",\n },\n};\n\nconst SOURCE_ICON: Record<string, typeof Brain> = {\n TIME_ENTRY: Zap,\n MEETING: Users,\n POMODORO: Brain,\n EMAIL: MessageSquare,\n CAPTURE: Github,\n};\n\nconst CATEGORY_ORDER: Category[] = [\n \"DEEP_WORK\",\n \"MEETING\",\n \"COMMUNICATION\",\n \"BREAK\",\n \"OTHER\",\n];\n\nfunction toLocalDateInput(date: Date): string {\n const offset = date.getTimezoneOffset();\n const local = new Date(date.getTime() - offset * 60 * 1000);\n return local.toISOString().slice(0, 10);\n}\n\nfunction dayBounds(dateStr: string): { from: string; to: string } {\n const start = new Date(`${dateStr}T00:00:00`);\n const end = new Date(`${dateStr}T23:59:59.999`);\n return { from: start.toISOString(), to: end.toISOString() };\n}\n\nexport function TimelinePage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const [dateStr, setDateStr] = useState(() => toLocalDateInput(new Date()));\n\n const { from, to } = useMemo(() => dayBounds(dateStr), [dateStr]);\n\n const { data, loading, error, refetch } = useGetTimeCampusTimelineQuery({\n variables: { from, to },\n skip: !workspaceId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const [recategorize, { loading: recategorizing }] =\n useRecategorizeTimeCampusCaptureEventMutation();\n\n const timeline = data?.timeCampusTimeline;\n const segments = timeline?.segments ?? [];\n const summary = timeline?.summary;\n\n const totalsByCategory = useMemo(() => {\n const map = new Map<string, { totalSeconds: number; segmentCount: number }>();\n for (const t of summary?.byCategory ?? []) {\n map.set(t.category, {\n totalSeconds: t.totalSeconds,\n segmentCount: t.segmentCount,\n });\n }\n return map;\n }, [summary]);\n\n const handleRecategorize = async (\n segment: TimelineSegmentFieldsFragment,\n category: Category,\n ) => {\n if (!segment.isRecategorizable || segment.category === category) return;\n await recategorize({\n variables: { id: segment.sourceId, category },\n });\n await refetch();\n };\n\n const focusPct =\n summary && summary.focusRatio != null\n ? Math.round(summary.focusRatio * 100)\n : 0;\n\n return (\n <PageLayout\n title=\"Timeline\"\n description=\"Your day, assembled automatically\"\n purpose=\"See where your time actually went — meetings, deep work, communication and breaks stitched into one honest, chronological view from everything TimeCampus captures, so you can spot the patterns worth changing.\"\n actions={\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Calendar className=\"h-4 w-4\" />\n <input\n type=\"date\"\n value={dateStr}\n max={toLocalDateInput(new Date())}\n onChange={(e) => setDateStr(e.target.value)}\n className=\"rounded-md border border-border-seam bg-bg-surface px-3 py-1.5 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40\"\n />\n </label>\n }\n >\n {loading && !timeline ? (\n <div className=\"space-y-6\">\n <LoadingSkeleton variant=\"card\" count={4} />\n <LoadingSkeleton variant=\"list\" count={6} />\n </div>\n ) : error ? (\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load your timeline. Please try again later.\n </div>\n ) : (\n <div className=\"space-y-6\">\n {/* Summary */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Tracked time\"\n value={formatDuration(summary?.totalTrackedSeconds ?? 0)}\n icon={<Activity className=\"h-5 w-5\" />}\n description=\"Total activity across the day\"\n />\n <StatCard\n title=\"Focus ratio\"\n value={`${focusPct}%`}\n icon={<Brain className=\"h-5 w-5\" />}\n description=\"Share of time in deep work\"\n />\n <StatCard\n title=\"Context switches\"\n value={summary?.contextSwitches ?? 0}\n icon={<Zap className=\"h-5 w-5\" />}\n description=\"Category changes through the day\"\n />\n <StatCard\n title=\"Longest focus block\"\n value={formatDuration(summary?.longestFocusBlockSeconds ?? 0)}\n icon={<Brain className=\"h-5 w-5\" />}\n description=\"Your deepest uninterrupted stretch\"\n />\n </div>\n\n {/* Category breakdown */}\n {segments.length > 0 && (\n <GlassCard treatment=\"glass\" className=\"p-4 sm:p-6\">\n <h2 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Where the time went\n </h2>\n <div className=\"flex flex-wrap gap-4\">\n {CATEGORY_ORDER.filter((c) => totalsByCategory.has(c)).map(\n (category) => {\n const meta = CATEGORY_META[category];\n const total = totalsByCategory.get(category)!;\n return (\n <div\n key={category}\n className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-2\"\n >\n <span\n className={`inline-block h-2.5 w-2.5 rounded-full ${meta.dot}`}\n aria-hidden\n />\n <span className=\"text-sm font-medium text-text-primary\">\n {meta.label}\n </span>\n <span className=\"text-sm tabular-nums text-text-secondary\">\n {formatDuration(total.totalSeconds)}\n </span>\n </div>\n );\n },\n )}\n </div>\n </GlassCard>\n )}\n\n {/* Chronological timeline */}\n {segments.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No activity captured for this day\"\n description=\"As you track time, hold meetings, run focus sessions, and connect capture channels, your day assembles here automatically — categorized and in order.\"\n />\n ) : (\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <ol className=\"divide-y divide-border-seam\">\n {segments.map((segment: TimelineSegmentFieldsFragment) => {\n const category = (segment.category as Category) ?? \"OTHER\";\n const meta = CATEGORY_META[category] ?? CATEGORY_META.OTHER;\n const SourceIcon = SOURCE_ICON[segment.source] ?? Activity;\n return (\n <li\n key={segment.id}\n className=\"flex items-start gap-3 p-4 transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"flex w-16 shrink-0 flex-col items-end pt-0.5 text-xs tabular-nums text-text-secondary\">\n <span>{formatTime(segment.startTime)}</span>\n <span className=\"text-text-tertiary\">\n {formatDuration(segment.durationSeconds)}\n </span>\n </div>\n <div\n className={`mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${meta.chip}`}\n >\n <SourceIcon className=\"h-4 w-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {segment.title || meta.label}\n </p>\n <div className=\"mt-1 flex flex-wrap items-center gap-2\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${meta.chip}`}\n >\n {meta.label}\n </span>\n {segment.app && (\n <span className=\"text-xs text-text-secondary\">\n {segment.app}\n </span>\n )}\n {segment.isRecategorizable && (\n <span className=\"text-xs text-text-tertiary\">\n via capture\n </span>\n )}\n </div>\n </div>\n {segment.isRecategorizable && (\n <select\n aria-label=\"Recategorize activity\"\n disabled={recategorizing}\n value={category}\n onChange={(e) =>\n handleRecategorize(\n segment,\n e.target.value as Category,\n )\n }\n className=\"shrink-0 rounded-md border border-border-seam bg-bg-surface px-2 py-1 text-xs text-text-secondary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40\"\n >\n {CATEGORY_ORDER.map((c) => (\n <option key={c} value={c}>\n {CATEGORY_META[c].label}\n </option>\n ))}\n </select>\n )}\n </li>\n );\n })}\n </ol>\n </GlassCard>\n )}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -5,34 +5,44 @@ function n(...n) {
|
|
|
5
5
|
return t(e(n));
|
|
6
6
|
}
|
|
7
7
|
function r(e) {
|
|
8
|
+
let t = Math.floor(e / 3600), n = Math.floor(e % 3600 / 60), r = e % 60;
|
|
9
|
+
return t > 0 ? `${t}h ${n}m ${r}s` : n > 0 ? `${n}m ${r}s` : `${r}s`;
|
|
10
|
+
}
|
|
11
|
+
function i(e) {
|
|
8
12
|
let t = Math.floor(e);
|
|
9
13
|
return `${t}h ${Math.round((e - t) * 60)}m`;
|
|
10
14
|
}
|
|
11
|
-
function
|
|
15
|
+
function a(e) {
|
|
12
16
|
let t = Math.floor(e / 3600), n = Math.floor(e % 3600 / 60), r = e % 60;
|
|
13
17
|
return `${String(t).padStart(2, "0")}:${String(n).padStart(2, "0")}:${String(r).padStart(2, "0")}`;
|
|
14
18
|
}
|
|
15
|
-
function
|
|
19
|
+
function o(e, t = "USD") {
|
|
16
20
|
return new Intl.NumberFormat("en-US", {
|
|
17
21
|
style: "currency",
|
|
18
22
|
currency: t
|
|
19
23
|
}).format(e);
|
|
20
24
|
}
|
|
21
|
-
function
|
|
25
|
+
function s(e) {
|
|
22
26
|
return new Date(e).toLocaleDateString("en-US", {
|
|
23
27
|
year: "numeric",
|
|
24
28
|
month: "short",
|
|
25
29
|
day: "numeric"
|
|
26
30
|
});
|
|
27
31
|
}
|
|
28
|
-
function s(e) {
|
|
29
|
-
let t = /* @__PURE__ */ new Date(), n = new Date(e), r = t.getTime() - n.getTime(), i = Math.floor(r / 6e4), a = Math.floor(r / 36e5), s = Math.floor(r / 864e5);
|
|
30
|
-
return i < 1 ? "Just now" : i < 60 ? `${i}m ago` : a < 24 ? `${a}h ago` : s < 7 ? `${s}d ago` : o(e);
|
|
31
|
-
}
|
|
32
32
|
function c(e) {
|
|
33
|
-
return
|
|
33
|
+
return new Date(e).toLocaleTimeString("en-US", {
|
|
34
|
+
hour: "2-digit",
|
|
35
|
+
minute: "2-digit"
|
|
36
|
+
});
|
|
34
37
|
}
|
|
35
38
|
function l(e) {
|
|
39
|
+
let t = /* @__PURE__ */ new Date(), n = new Date(e), r = t.getTime() - n.getTime(), i = Math.floor(r / 6e4), a = Math.floor(r / 36e5), o = Math.floor(r / 864e5);
|
|
40
|
+
return i < 1 ? "Just now" : i < 60 ? `${i}m ago` : a < 24 ? `${a}h ago` : o < 7 ? `${o}d ago` : s(e);
|
|
41
|
+
}
|
|
42
|
+
function u(e) {
|
|
43
|
+
return e.split(" ").map((e) => e[0]).join("").toUpperCase().slice(0, 2);
|
|
44
|
+
}
|
|
45
|
+
function d(e) {
|
|
36
46
|
return {
|
|
37
47
|
active: "success",
|
|
38
48
|
completed: "info",
|
|
@@ -58,7 +68,7 @@ function l(e) {
|
|
|
58
68
|
flagged: "error"
|
|
59
69
|
}[e] || "default";
|
|
60
70
|
}
|
|
61
|
-
function
|
|
71
|
+
function f(e) {
|
|
62
72
|
return {
|
|
63
73
|
low: "info",
|
|
64
74
|
medium: "warning",
|
|
@@ -68,6 +78,6 @@ function u(e) {
|
|
|
68
78
|
}[e] || "default";
|
|
69
79
|
}
|
|
70
80
|
//#endregion
|
|
71
|
-
export { n as cn,
|
|
81
|
+
export { n as cn, o as formatCurrency, s as formatDate, r as formatDuration, i as formatHours, l as formatRelativeTime, c as formatTime, a as formatTimerDuration, u as getInitials, f as getPriorityColor, d as getStatusColor };
|
|
72
82
|
|
|
73
83
|
//# sourceMappingURL=helpers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","names":[],"sources":["../../../src/timecampus/utils/helpers.ts"],"sourcesContent":["/**\n * TimeCampus Utility Helpers\n * Common formatting, styling, and utility functions.\n * @module timecampus\n */\n\nimport { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport type BadgeVariant =\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"default\"\n | \"muted\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport function formatDuration(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = seconds % 60;\n if (h > 0) return `${h}h ${m}m ${s}s`;\n if (m > 0) return `${m}m ${s}s`;\n return `${s}s`;\n}\n\nexport function formatHours(hours: number): string {\n const h = Math.floor(hours);\n const m = Math.round((hours - h) * 60);\n return `${h}h ${m}m`;\n}\n\nexport function formatTimerDuration(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = seconds % 60;\n return `${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n}\n\nexport function formatCurrency(amount: number, currency = \"USD\"): string {\n return new Intl.NumberFormat(\"en-US\", { style: \"currency\", currency }).format(\n amount,\n );\n}\n\nexport function formatNumber(num: number): string {\n return new Intl.NumberFormat(\"en-US\").format(num);\n}\n\nexport function formatDate(date: string | Date): string {\n return new Date(date).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n}\n\nexport function formatTime(date: string | Date): string {\n return new Date(date).toLocaleTimeString(\"en-US\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n}\n\nexport function formatRelativeTime(date: string | Date): string {\n const now = new Date();\n const d = new Date(date);\n const diffMs = now.getTime() - d.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n if (diffMins < 1) return \"Just now\";\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n return formatDate(date);\n}\n\nexport function getInitials(name: string): string {\n return name\n .split(\" \")\n .map((n) => n[0])\n .join(\"\")\n .toUpperCase()\n .slice(0, 2);\n}\n\nexport function truncateText(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return text.slice(0, maxLength) + \"...\";\n}\n\nexport function generateId(): string {\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function getStatusColor(status: string): BadgeVariant {\n const colors: Record<string, BadgeVariant> = {\n active: \"success\",\n completed: \"info\",\n \"in-progress\": \"warning\",\n pending: \"warning\",\n archived: \"muted\",\n \"on-hold\": \"warning\",\n draft: \"muted\",\n approved: \"success\",\n rejected: \"error\",\n error: \"error\",\n running: \"success\",\n paused: \"warning\",\n cancelled: \"muted\",\n online: \"success\",\n away: \"warning\",\n busy: \"error\",\n offline: \"muted\",\n inactive: \"muted\",\n submitted: \"info\",\n reimbursed: \"success\",\n \"revision-requested\": \"warning\",\n flagged: \"error\",\n };\n return colors[status] || \"default\";\n}\n\nexport function getPriorityColor(priority: string): BadgeVariant {\n const colors: Record<string, BadgeVariant> = {\n low: \"info\",\n medium: \"warning\",\n high: \"warning\",\n urgent: \"error\",\n critical: \"error\",\n };\n return colors[priority] || \"default\";\n}\n"],"mappings":";;;AAiBA,SAAgB,EAAG,GAAG,GAAsB;AAC1C,QAAO,EAAQ,EAAK,EAAO,CAAC;;
|
|
1
|
+
{"version":3,"file":"helpers.js","names":[],"sources":["../../../src/timecampus/utils/helpers.ts"],"sourcesContent":["/**\n * TimeCampus Utility Helpers\n * Common formatting, styling, and utility functions.\n * @module timecampus\n */\n\nimport { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport type BadgeVariant =\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\"\n | \"default\"\n | \"muted\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport function formatDuration(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = seconds % 60;\n if (h > 0) return `${h}h ${m}m ${s}s`;\n if (m > 0) return `${m}m ${s}s`;\n return `${s}s`;\n}\n\nexport function formatHours(hours: number): string {\n const h = Math.floor(hours);\n const m = Math.round((hours - h) * 60);\n return `${h}h ${m}m`;\n}\n\nexport function formatTimerDuration(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = seconds % 60;\n return `${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n}\n\nexport function formatCurrency(amount: number, currency = \"USD\"): string {\n return new Intl.NumberFormat(\"en-US\", { style: \"currency\", currency }).format(\n amount,\n );\n}\n\nexport function formatNumber(num: number): string {\n return new Intl.NumberFormat(\"en-US\").format(num);\n}\n\nexport function formatDate(date: string | Date): string {\n return new Date(date).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n}\n\nexport function formatTime(date: string | Date): string {\n return new Date(date).toLocaleTimeString(\"en-US\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n}\n\nexport function formatRelativeTime(date: string | Date): string {\n const now = new Date();\n const d = new Date(date);\n const diffMs = now.getTime() - d.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n if (diffMins < 1) return \"Just now\";\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n return formatDate(date);\n}\n\nexport function getInitials(name: string): string {\n return name\n .split(\" \")\n .map((n) => n[0])\n .join(\"\")\n .toUpperCase()\n .slice(0, 2);\n}\n\nexport function truncateText(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return text.slice(0, maxLength) + \"...\";\n}\n\nexport function generateId(): string {\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function getStatusColor(status: string): BadgeVariant {\n const colors: Record<string, BadgeVariant> = {\n active: \"success\",\n completed: \"info\",\n \"in-progress\": \"warning\",\n pending: \"warning\",\n archived: \"muted\",\n \"on-hold\": \"warning\",\n draft: \"muted\",\n approved: \"success\",\n rejected: \"error\",\n error: \"error\",\n running: \"success\",\n paused: \"warning\",\n cancelled: \"muted\",\n online: \"success\",\n away: \"warning\",\n busy: \"error\",\n offline: \"muted\",\n inactive: \"muted\",\n submitted: \"info\",\n reimbursed: \"success\",\n \"revision-requested\": \"warning\",\n flagged: \"error\",\n };\n return colors[status] || \"default\";\n}\n\nexport function getPriorityColor(priority: string): BadgeVariant {\n const colors: Record<string, BadgeVariant> = {\n low: \"info\",\n medium: \"warning\",\n high: \"warning\",\n urgent: \"error\",\n critical: \"error\",\n };\n return colors[priority] || \"default\";\n}\n"],"mappings":";;;AAiBA,SAAgB,EAAG,GAAG,GAAsB;AAC1C,QAAO,EAAQ,EAAK,EAAO,CAAC;;AAG9B,SAAgB,EAAe,GAAyB;CACtD,IAAM,IAAI,KAAK,MAAM,IAAU,KAAK,EAC9B,IAAI,KAAK,MAAO,IAAU,OAAQ,GAAG,EACrC,IAAI,IAAU;AAGpB,QAFI,IAAI,IAAU,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAC/B,IAAI,IAAU,GAAG,EAAE,IAAI,EAAE,KACtB,GAAG,EAAE;;AAGd,SAAgB,EAAY,GAAuB;CACjD,IAAM,IAAI,KAAK,MAAM,EAAM;AAE3B,QAAO,GAAG,EAAE,IADF,KAAK,OAAO,IAAQ,KAAK,GAAG,CACpB;;AAGpB,SAAgB,EAAoB,GAAyB;CAC3D,IAAM,IAAI,KAAK,MAAM,IAAU,KAAK,EAC9B,IAAI,KAAK,MAAO,IAAU,OAAQ,GAAG,EACrC,IAAI,IAAU;AACpB,QAAO,GAAG,OAAO,EAAE,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC,SAAS,GAAG,IAAI;;AAGlG,SAAgB,EAAe,GAAgB,IAAW,OAAe;AACvE,QAAO,IAAI,KAAK,aAAa,SAAS;EAAE,OAAO;EAAY;EAAU,CAAC,CAAC,OACrE,EACD;;AAOH,SAAgB,EAAW,GAA6B;AACtD,QAAO,IAAI,KAAK,EAAK,CAAC,mBAAmB,SAAS;EAChD,MAAM;EACN,OAAO;EACP,KAAK;EACN,CAAC;;AAGJ,SAAgB,EAAW,GAA6B;AACtD,QAAO,IAAI,KAAK,EAAK,CAAC,mBAAmB,SAAS;EAChD,MAAM;EACN,QAAQ;EACT,CAAC;;AAGJ,SAAgB,EAAmB,GAA6B;CAC9D,IAAM,oBAAM,IAAI,MAAM,EAChB,IAAI,IAAI,KAAK,EAAK,EAClB,IAAS,EAAI,SAAS,GAAG,EAAE,SAAS,EACpC,IAAW,KAAK,MAAM,IAAS,IAAM,EACrC,IAAY,KAAK,MAAM,IAAS,KAAQ,EACxC,IAAW,KAAK,MAAM,IAAS,MAAS;AAK9C,QAJI,IAAW,IAAU,aACrB,IAAW,KAAW,GAAG,EAAS,SAClC,IAAY,KAAW,GAAG,EAAU,SACpC,IAAW,IAAU,GAAG,EAAS,SAC9B,EAAW,EAAK;;AAGzB,SAAgB,EAAY,GAAsB;AAChD,QAAO,EACJ,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,GAAG,CAChB,KAAK,GAAG,CACR,aAAa,CACb,MAAM,GAAG,EAAE;;AAgBhB,SAAgB,EAAe,GAA8B;AAyB3D,QAxB6C;EAC3C,QAAQ;EACR,WAAW;EACX,eAAe;EACf,SAAS;EACT,UAAU;EACV,WAAW;EACX,OAAO;EACP,UAAU;EACV,UAAU;EACV,OAAO;EACP,SAAS;EACT,QAAQ;EACR,WAAW;EACX,QAAQ;EACR,MAAM;EACN,MAAM;EACN,SAAS;EACT,UAAU;EACV,WAAW;EACX,YAAY;EACZ,sBAAsB;EACtB,SAAS;EACV,CACa,MAAW;;AAG3B,SAAgB,EAAiB,GAAgC;AAQ/D,QAP6C;EAC3C,KAAK;EACL,QAAQ;EACR,MAAM;EACN,QAAQ;EACR,UAAU;EACX,CACa,MAAa"}
|
package/package.json
CHANGED