@saas-maker/progress 0.1.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 ADDED
@@ -0,0 +1,33 @@
1
+ # @saas-maker/progress
2
+
3
+ Embeddable public progress widget for SaaS Maker projects.
4
+
5
+ It combines:
6
+
7
+ - published changelog entries
8
+ - public roadmap items grouped as In Progress, Planned, and Shipped
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @saas-maker/progress
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ```tsx
19
+ import { ProgressWidget } from '@saas-maker/progress';
20
+ import '@saas-maker/progress/dist/index.css';
21
+
22
+ <ProgressWidget slug="my-project" />
23
+ ```
24
+
25
+ ## Props
26
+
27
+ | Prop | Type | Default |
28
+ |---|---|---|
29
+ | `slug` | `string` | required |
30
+ | `apiBaseUrl` | `string` | `https://api.sassmaker.com` |
31
+ | `theme` | `light \| dark \| auto` | `auto` |
32
+ | `maxChangelogItems` | `number` | API default |
33
+ | `showEmptyStates` | `boolean` | `true` |
package/dist/index.css ADDED
@@ -0,0 +1,210 @@
1
+ /* src/styles/progress.css */
2
+ [data-saasmaker-progress] {
3
+ --smp-bg: #ffffff;
4
+ --smp-card: #f8fafc;
5
+ --smp-border: #e2e8f0;
6
+ --smp-text: #0f172a;
7
+ --smp-muted: #64748b;
8
+ --smp-subtle: #94a3b8;
9
+ --smp-accent: #4f46e5;
10
+ --smp-green: #16a34a;
11
+ --smp-amber: #d97706;
12
+ --smp-blue: #2563eb;
13
+ color: var(--smp-text);
14
+ font-family:
15
+ Inter,
16
+ ui-sans-serif,
17
+ system-ui,
18
+ -apple-system,
19
+ BlinkMacSystemFont,
20
+ "Segoe UI",
21
+ sans-serif;
22
+ }
23
+ [data-saasmaker-progress].smp--dark {
24
+ --smp-bg: #020617;
25
+ --smp-card: #0f172a;
26
+ --smp-border: #1e293b;
27
+ --smp-text: #f8fafc;
28
+ --smp-muted: #94a3b8;
29
+ --smp-subtle: #64748b;
30
+ --smp-accent: #818cf8;
31
+ --smp-green: #4ade80;
32
+ --smp-amber: #fbbf24;
33
+ --smp-blue: #60a5fa;
34
+ }
35
+ @media (prefers-color-scheme: dark) {
36
+ [data-saasmaker-progress].smp--auto {
37
+ --smp-bg: #020617;
38
+ --smp-card: #0f172a;
39
+ --smp-border: #1e293b;
40
+ --smp-text: #f8fafc;
41
+ --smp-muted: #94a3b8;
42
+ --smp-subtle: #64748b;
43
+ --smp-accent: #818cf8;
44
+ --smp-green: #4ade80;
45
+ --smp-amber: #fbbf24;
46
+ --smp-blue: #60a5fa;
47
+ }
48
+ }
49
+ [data-saasmaker-progress] .smp-header {
50
+ margin-bottom: 1rem;
51
+ }
52
+ [data-saasmaker-progress] .smp-header p,
53
+ [data-saasmaker-progress] .smp-section-header h3 {
54
+ margin: 0;
55
+ }
56
+ [data-saasmaker-progress] .smp-header p {
57
+ color: var(--smp-muted);
58
+ font-size: 0.75rem;
59
+ font-weight: 700;
60
+ letter-spacing: 0.08em;
61
+ text-transform: uppercase;
62
+ }
63
+ [data-saasmaker-progress] .smp-header h3 {
64
+ margin: 0.25rem 0 0;
65
+ font-size: 1.375rem;
66
+ line-height: 1.2;
67
+ }
68
+ [data-saasmaker-progress] .smp-layout {
69
+ display: grid;
70
+ gap: 1rem;
71
+ }
72
+ [data-saasmaker-progress] .smp-section {
73
+ background: var(--smp-bg);
74
+ border: 1px solid var(--smp-border);
75
+ border-radius: 0.75rem;
76
+ padding: 1rem;
77
+ }
78
+ [data-saasmaker-progress] .smp-section-header {
79
+ align-items: center;
80
+ display: flex;
81
+ justify-content: space-between;
82
+ margin-bottom: 0.875rem;
83
+ }
84
+ [data-saasmaker-progress] .smp-section-header h3 {
85
+ font-size: 0.95rem;
86
+ font-weight: 700;
87
+ }
88
+ [data-saasmaker-progress] .smp-roadmap {
89
+ display: grid;
90
+ gap: 0.75rem;
91
+ }
92
+ @media (min-width: 720px) {
93
+ [data-saasmaker-progress] .smp-roadmap {
94
+ grid-template-columns: repeat(3, minmax(0, 1fr));
95
+ }
96
+ }
97
+ [data-saasmaker-progress] .smp-roadmap-column {
98
+ background: var(--smp-card);
99
+ border: 1px solid var(--smp-border);
100
+ border-radius: 0.5rem;
101
+ padding: 0.75rem;
102
+ }
103
+ [data-saasmaker-progress] .smp-column-header {
104
+ align-items: center;
105
+ display: flex;
106
+ gap: 0.5rem;
107
+ justify-content: space-between;
108
+ margin-bottom: 0.625rem;
109
+ }
110
+ [data-saasmaker-progress] .smp-column-header h4 {
111
+ font-size: 0.8rem;
112
+ margin: 0;
113
+ }
114
+ [data-saasmaker-progress] .smp-column-header span {
115
+ color: var(--smp-muted);
116
+ font-size: 0.75rem;
117
+ font-variant-numeric: tabular-nums;
118
+ }
119
+ [data-saasmaker-progress] .smp-roadmap-items {
120
+ display: grid;
121
+ gap: 0.5rem;
122
+ }
123
+ [data-saasmaker-progress] .smp-roadmap-item {
124
+ align-items: start;
125
+ background: var(--smp-bg);
126
+ border: 1px solid var(--smp-border);
127
+ border-radius: 0.5rem;
128
+ display: flex;
129
+ gap: 0.75rem;
130
+ justify-content: space-between;
131
+ padding: 0.625rem;
132
+ }
133
+ [data-saasmaker-progress] .smp-roadmap-item h5,
134
+ [data-saasmaker-progress] .smp-changelog-item h4 {
135
+ font-size: 0.875rem;
136
+ line-height: 1.3;
137
+ margin: 0;
138
+ }
139
+ [data-saasmaker-progress] .smp-roadmap-item p,
140
+ [data-saasmaker-progress] .smp-changelog-item p {
141
+ color: var(--smp-muted);
142
+ font-size: 0.8rem;
143
+ line-height: 1.45;
144
+ margin: 0.25rem 0 0;
145
+ }
146
+ [data-saasmaker-progress] .smp-votes {
147
+ color: var(--smp-accent);
148
+ font-size: 0.75rem;
149
+ font-weight: 700;
150
+ }
151
+ [data-saasmaker-progress] .smp-changelog {
152
+ border-left: 1px solid var(--smp-border);
153
+ display: grid;
154
+ gap: 1rem;
155
+ list-style: none;
156
+ margin: 0;
157
+ padding: 0 0 0 1rem;
158
+ }
159
+ [data-saasmaker-progress] .smp-changelog-item {
160
+ position: relative;
161
+ }
162
+ [data-saasmaker-progress] .smp-changelog-item::before {
163
+ background: var(--smp-accent);
164
+ border: 2px solid var(--smp-bg);
165
+ border-radius: 999px;
166
+ content: "";
167
+ height: 0.55rem;
168
+ left: -1.3rem;
169
+ position: absolute;
170
+ top: 0.35rem;
171
+ width: 0.55rem;
172
+ }
173
+ [data-saasmaker-progress] .smp-changelog-meta {
174
+ align-items: center;
175
+ color: var(--smp-subtle);
176
+ display: flex;
177
+ flex-wrap: wrap;
178
+ font-size: 0.72rem;
179
+ gap: 0.4rem;
180
+ margin-bottom: 0.25rem;
181
+ }
182
+ [data-saasmaker-progress] .smp-badge,
183
+ [data-saasmaker-progress] .smp-version {
184
+ border: 1px solid var(--smp-border);
185
+ border-radius: 999px;
186
+ font-size: 0.68rem;
187
+ font-weight: 700;
188
+ padding: 0.125rem 0.4rem;
189
+ text-transform: capitalize;
190
+ }
191
+ [data-saasmaker-progress] .smp-badge--feature {
192
+ color: var(--smp-green);
193
+ }
194
+ [data-saasmaker-progress] .smp-badge--improvement {
195
+ color: var(--smp-blue);
196
+ }
197
+ [data-saasmaker-progress] .smp-badge--fix {
198
+ color: var(--smp-amber);
199
+ }
200
+ [data-saasmaker-progress] .smp-badge--breaking {
201
+ color: #ef4444;
202
+ }
203
+ [data-saasmaker-progress] .smp-empty {
204
+ color: var(--smp-muted);
205
+ font-size: 0.875rem;
206
+ margin: 0;
207
+ }
208
+ [data-saasmaker-progress] .smp-empty--compact {
209
+ font-size: 0.78rem;
210
+ }
package/dist/index.js ADDED
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ ProgressWidget: () => ProgressWidget
24
+ });
25
+ module.exports = __toCommonJS(src_exports);
26
+
27
+ // src/ProgressWidget.tsx
28
+ var import_react = require("react");
29
+
30
+ // src/api.ts
31
+ var DEFAULT_API_BASE = "https://api.sassmaker.com";
32
+ function createApiClient(apiBaseUrl) {
33
+ const base = (apiBaseUrl || DEFAULT_API_BASE).replace(/\/$/, "");
34
+ return {
35
+ async getPublicProgress(slug, changelogLimit) {
36
+ const params = new URLSearchParams();
37
+ if (changelogLimit)
38
+ params.set("changelog_limit", String(changelogLimit));
39
+ const qs = params.toString();
40
+ const res = await fetch(`${base}/v1/progress/public/${encodeURIComponent(slug)}${qs ? `?${qs}` : ""}`);
41
+ if (!res.ok)
42
+ return null;
43
+ return res.json();
44
+ }
45
+ };
46
+ }
47
+
48
+ // src/ProgressWidget.tsx
49
+ var import_jsx_runtime = require("react/jsx-runtime");
50
+ var ROADMAP_COLUMNS = ["in_progress", "planned", "done"];
51
+ var COLUMN_LABELS = {
52
+ in_progress: "In Progress",
53
+ planned: "Planned",
54
+ done: "Shipped"
55
+ };
56
+ function formatDate(dateStr) {
57
+ if (!dateStr)
58
+ return "";
59
+ const date = new Date(dateStr);
60
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
61
+ }
62
+ function getColumnItems(items, column) {
63
+ return items.filter((item) => item.column === column).sort((a, b) => a.position - b.position || b.upvote_count - a.upvote_count);
64
+ }
65
+ function ChangelogList({ entries, showEmptyStates }) {
66
+ if (entries.length === 0) {
67
+ return showEmptyStates ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "smp-empty", children: "No shipped updates yet." }) : null;
68
+ }
69
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "smp-changelog", children: entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "smp-changelog-item", children: [
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smp-changelog-meta", children: [
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatDate(entry.published_at || entry.created_at) }),
72
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `smp-badge smp-badge--${entry.type}`, children: entry.type }),
73
+ entry.version && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "smp-version", children: [
74
+ "v",
75
+ entry.version
76
+ ] })
77
+ ] }),
78
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { children: entry.title }),
79
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: entry.content })
80
+ ] }, entry.id)) });
81
+ }
82
+ function RoadmapColumn({
83
+ label,
84
+ items,
85
+ showEmptyStates
86
+ }) {
87
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "smp-roadmap-column", children: [
88
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smp-column-header", children: [
89
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { children: label }),
90
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: items.length })
91
+ ] }),
92
+ items.length === 0 && showEmptyStates ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "smp-empty smp-empty--compact", children: "Nothing here yet." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smp-roadmap-items", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "smp-roadmap-item", children: [
93
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
94
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h5", { children: item.title }),
95
+ item.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: item.description })
96
+ ] }),
97
+ item.upvote_count > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smp-votes", children: item.upvote_count })
98
+ ] }, item.id)) })
99
+ ] });
100
+ }
101
+ function ProgressWidget({
102
+ slug,
103
+ apiBaseUrl,
104
+ theme = "auto",
105
+ maxChangelogItems,
106
+ showEmptyStates = true
107
+ }) {
108
+ const [data, setData] = (0, import_react.useState)(null);
109
+ const [loading, setLoading] = (0, import_react.useState)(true);
110
+ const api = (0, import_react.useMemo)(() => createApiClient(apiBaseUrl), [apiBaseUrl]);
111
+ const themeClass = theme === "light" ? "smp--light" : theme === "dark" ? "smp--dark" : "smp--auto";
112
+ (0, import_react.useEffect)(() => {
113
+ let cancelled = false;
114
+ setLoading(true);
115
+ api.getPublicProgress(slug, maxChangelogItems).then((result) => {
116
+ if (!cancelled && result) {
117
+ setData({
118
+ projectName: result.project.name,
119
+ changelog: result.changelog,
120
+ roadmap: result.roadmap
121
+ });
122
+ }
123
+ }).catch(() => {
124
+ }).finally(() => {
125
+ if (!cancelled)
126
+ setLoading(false);
127
+ });
128
+ return () => {
129
+ cancelled = true;
130
+ };
131
+ }, [api, maxChangelogItems, slug]);
132
+ if (loading)
133
+ return null;
134
+ if (!data) {
135
+ return showEmptyStates ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-saasmaker-progress": "", className: themeClass, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "smp-empty", children: "Progress is not available." }) }) : null;
136
+ }
137
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-saasmaker-progress": "", className: themeClass, children: [
138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "smp-header", children: [
139
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Progress" }),
140
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: data.projectName })
141
+ ] }),
142
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smp-layout", children: [
143
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "smp-section", children: [
144
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smp-section-header", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "Now and Next" }) }),
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smp-roadmap", children: ROADMAP_COLUMNS.map((column) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
146
+ RoadmapColumn,
147
+ {
148
+ label: COLUMN_LABELS[column],
149
+ items: getColumnItems(data.roadmap, column),
150
+ showEmptyStates
151
+ },
152
+ column
153
+ )) })
154
+ ] }),
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "smp-section", children: [
156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smp-section-header", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "Shipped" }) }),
157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChangelogList, { entries: data.changelog, showEmptyStates })
158
+ ] })
159
+ ] })
160
+ ] });
161
+ }
162
+ // Annotate the CommonJS export names for ESM import in node:
163
+ 0 && (module.exports = {
164
+ ProgressWidget
165
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,138 @@
1
+ // src/ProgressWidget.tsx
2
+ import { useEffect, useMemo, useState } from "react";
3
+
4
+ // src/api.ts
5
+ var DEFAULT_API_BASE = "https://api.sassmaker.com";
6
+ function createApiClient(apiBaseUrl) {
7
+ const base = (apiBaseUrl || DEFAULT_API_BASE).replace(/\/$/, "");
8
+ return {
9
+ async getPublicProgress(slug, changelogLimit) {
10
+ const params = new URLSearchParams();
11
+ if (changelogLimit)
12
+ params.set("changelog_limit", String(changelogLimit));
13
+ const qs = params.toString();
14
+ const res = await fetch(`${base}/v1/progress/public/${encodeURIComponent(slug)}${qs ? `?${qs}` : ""}`);
15
+ if (!res.ok)
16
+ return null;
17
+ return res.json();
18
+ }
19
+ };
20
+ }
21
+
22
+ // src/ProgressWidget.tsx
23
+ import { jsx, jsxs } from "react/jsx-runtime";
24
+ var ROADMAP_COLUMNS = ["in_progress", "planned", "done"];
25
+ var COLUMN_LABELS = {
26
+ in_progress: "In Progress",
27
+ planned: "Planned",
28
+ done: "Shipped"
29
+ };
30
+ function formatDate(dateStr) {
31
+ if (!dateStr)
32
+ return "";
33
+ const date = new Date(dateStr);
34
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
35
+ }
36
+ function getColumnItems(items, column) {
37
+ return items.filter((item) => item.column === column).sort((a, b) => a.position - b.position || b.upvote_count - a.upvote_count);
38
+ }
39
+ function ChangelogList({ entries, showEmptyStates }) {
40
+ if (entries.length === 0) {
41
+ return showEmptyStates ? /* @__PURE__ */ jsx("p", { className: "smp-empty", children: "No shipped updates yet." }) : null;
42
+ }
43
+ return /* @__PURE__ */ jsx("ol", { className: "smp-changelog", children: entries.map((entry) => /* @__PURE__ */ jsxs("li", { className: "smp-changelog-item", children: [
44
+ /* @__PURE__ */ jsxs("div", { className: "smp-changelog-meta", children: [
45
+ /* @__PURE__ */ jsx("span", { children: formatDate(entry.published_at || entry.created_at) }),
46
+ /* @__PURE__ */ jsx("span", { className: `smp-badge smp-badge--${entry.type}`, children: entry.type }),
47
+ entry.version && /* @__PURE__ */ jsxs("span", { className: "smp-version", children: [
48
+ "v",
49
+ entry.version
50
+ ] })
51
+ ] }),
52
+ /* @__PURE__ */ jsx("h4", { children: entry.title }),
53
+ /* @__PURE__ */ jsx("p", { children: entry.content })
54
+ ] }, entry.id)) });
55
+ }
56
+ function RoadmapColumn({
57
+ label,
58
+ items,
59
+ showEmptyStates
60
+ }) {
61
+ return /* @__PURE__ */ jsxs("section", { className: "smp-roadmap-column", children: [
62
+ /* @__PURE__ */ jsxs("div", { className: "smp-column-header", children: [
63
+ /* @__PURE__ */ jsx("h4", { children: label }),
64
+ /* @__PURE__ */ jsx("span", { children: items.length })
65
+ ] }),
66
+ items.length === 0 && showEmptyStates ? /* @__PURE__ */ jsx("p", { className: "smp-empty smp-empty--compact", children: "Nothing here yet." }) : /* @__PURE__ */ jsx("div", { className: "smp-roadmap-items", children: items.map((item) => /* @__PURE__ */ jsxs("article", { className: "smp-roadmap-item", children: [
67
+ /* @__PURE__ */ jsxs("div", { children: [
68
+ /* @__PURE__ */ jsx("h5", { children: item.title }),
69
+ item.description && /* @__PURE__ */ jsx("p", { children: item.description })
70
+ ] }),
71
+ item.upvote_count > 0 && /* @__PURE__ */ jsx("span", { className: "smp-votes", children: item.upvote_count })
72
+ ] }, item.id)) })
73
+ ] });
74
+ }
75
+ function ProgressWidget({
76
+ slug,
77
+ apiBaseUrl,
78
+ theme = "auto",
79
+ maxChangelogItems,
80
+ showEmptyStates = true
81
+ }) {
82
+ const [data, setData] = useState(null);
83
+ const [loading, setLoading] = useState(true);
84
+ const api = useMemo(() => createApiClient(apiBaseUrl), [apiBaseUrl]);
85
+ const themeClass = theme === "light" ? "smp--light" : theme === "dark" ? "smp--dark" : "smp--auto";
86
+ useEffect(() => {
87
+ let cancelled = false;
88
+ setLoading(true);
89
+ api.getPublicProgress(slug, maxChangelogItems).then((result) => {
90
+ if (!cancelled && result) {
91
+ setData({
92
+ projectName: result.project.name,
93
+ changelog: result.changelog,
94
+ roadmap: result.roadmap
95
+ });
96
+ }
97
+ }).catch(() => {
98
+ }).finally(() => {
99
+ if (!cancelled)
100
+ setLoading(false);
101
+ });
102
+ return () => {
103
+ cancelled = true;
104
+ };
105
+ }, [api, maxChangelogItems, slug]);
106
+ if (loading)
107
+ return null;
108
+ if (!data) {
109
+ return showEmptyStates ? /* @__PURE__ */ jsx("div", { "data-saasmaker-progress": "", className: themeClass, children: /* @__PURE__ */ jsx("p", { className: "smp-empty", children: "Progress is not available." }) }) : null;
110
+ }
111
+ return /* @__PURE__ */ jsxs("div", { "data-saasmaker-progress": "", className: themeClass, children: [
112
+ /* @__PURE__ */ jsxs("header", { className: "smp-header", children: [
113
+ /* @__PURE__ */ jsx("p", { children: "Progress" }),
114
+ /* @__PURE__ */ jsx("h3", { children: data.projectName })
115
+ ] }),
116
+ /* @__PURE__ */ jsxs("div", { className: "smp-layout", children: [
117
+ /* @__PURE__ */ jsxs("section", { className: "smp-section", children: [
118
+ /* @__PURE__ */ jsx("div", { className: "smp-section-header", children: /* @__PURE__ */ jsx("h3", { children: "Now and Next" }) }),
119
+ /* @__PURE__ */ jsx("div", { className: "smp-roadmap", children: ROADMAP_COLUMNS.map((column) => /* @__PURE__ */ jsx(
120
+ RoadmapColumn,
121
+ {
122
+ label: COLUMN_LABELS[column],
123
+ items: getColumnItems(data.roadmap, column),
124
+ showEmptyStates
125
+ },
126
+ column
127
+ )) })
128
+ ] }),
129
+ /* @__PURE__ */ jsxs("section", { className: "smp-section", children: [
130
+ /* @__PURE__ */ jsx("div", { className: "smp-section-header", children: /* @__PURE__ */ jsx("h3", { children: "Shipped" }) }),
131
+ /* @__PURE__ */ jsx(ChangelogList, { entries: data.changelog, showEmptyStates })
132
+ ] })
133
+ ] })
134
+ ] });
135
+ }
136
+ export {
137
+ ProgressWidget
138
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@saas-maker/progress",
3
+ "version": "0.1.0",
4
+ "description": "Embeddable product progress widget for SaaS Maker public projects.",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "main": "dist/index.js",
12
+ "module": "dist/index.mjs",
13
+ "types": "dist/index.d.ts",
14
+ "scripts": {
15
+ "build": "tsup src/index.ts --format esm,cjs --dts --external react --external react-dom",
16
+ "typecheck": "tsc --noEmit"
17
+ },
18
+ "dependencies": {
19
+ "@saas-maker/shared-types": "workspace:*"
20
+ },
21
+ "peerDependencies": {
22
+ "react": "19.2.5",
23
+ "react-dom": "19.2.5"
24
+ },
25
+ "devDependencies": {
26
+ "@types/react": "19",
27
+ "@types/react-dom": "19",
28
+ "react": "19.2.5",
29
+ "react-dom": "19.2.5",
30
+ "tsup": "8.0.0",
31
+ "typescript": "6.0.3"
32
+ }
33
+ }