@ossy/package-catalog 3.0.6 → 3.0.7
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/package.json +2 -2
- package/src/Definition.js +1 -0
- package/src/PackagesBrowser.jsx +39 -20
- package/src/en.translations.json +6 -1
- package/src/index.js +2 -0
- package/src/package-detail.page.jsx +11 -9
- package/src/packageCopy.js +49 -1
- package/src/sv.translations.json +6 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/package-catalog",
|
|
3
3
|
"description": "Capability catalog UI — browse manifest packages, actions, and integration examples",
|
|
4
|
-
"version": "3.0.
|
|
4
|
+
"version": "3.0.7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"module": "./src/index.js",
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"@ossy/workspaces": ">=1.0.0",
|
|
30
30
|
"react": ">=19.0.0 <20.0.0"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "5010f51795d5b8f4c63ef52495f4b2935b801b3f"
|
|
33
33
|
}
|
package/src/Definition.js
CHANGED
package/src/PackagesBrowser.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react'
|
|
2
|
-
import { Button, Icon, Input, Page, ContentHeader, SdkInvokeExample, Text, View } from '@ossy/design-system'
|
|
2
|
+
import { Button, Icon, Input, Page, ContentHeader, SdkInvokeExample, Text, View, useLocale } from '@ossy/design-system'
|
|
3
3
|
import { useApp } from '@ossy/app/shell'
|
|
4
4
|
import { useRouter } from '@ossy/router-react'
|
|
5
5
|
import { PackageHero } from './PackageHero.jsx'
|
|
@@ -10,21 +10,22 @@ import {
|
|
|
10
10
|
} from './ActionCapabilityCard.jsx'
|
|
11
11
|
import {
|
|
12
12
|
getPackageIcon,
|
|
13
|
-
getPackagePitch,
|
|
14
13
|
humanizeCapabilityId,
|
|
15
|
-
|
|
14
|
+
resolvePackageDisplayName,
|
|
15
|
+
resolvePackagePitch,
|
|
16
16
|
} from './packageCopy.js'
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* @param {object} pkg
|
|
20
|
+
* @param {{ t: (key: string) => string, definition?: object | null }} options
|
|
20
21
|
* @returns {string}
|
|
21
22
|
*/
|
|
22
|
-
function packageSearchHaystack (pkg) {
|
|
23
|
+
function packageSearchHaystack (pkg, { t, definition }) {
|
|
23
24
|
const parts = [
|
|
24
25
|
pkg.package,
|
|
25
26
|
pkg.slug,
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
resolvePackageDisplayName(pkg, { t, definition }),
|
|
28
|
+
resolvePackagePitch(pkg, { t }),
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
for (const page of pkg.pages || []) {
|
|
@@ -47,12 +48,16 @@ function packageSearchHaystack (pkg) {
|
|
|
47
48
|
/**
|
|
48
49
|
* @param {object[]} packages
|
|
49
50
|
* @param {string} query
|
|
51
|
+
* @param {{ t: (key: string) => string, definitions?: Record<string, object> }} options
|
|
50
52
|
* @returns {object[]}
|
|
51
53
|
*/
|
|
52
|
-
function filterPackages (packages, query) {
|
|
54
|
+
function filterPackages (packages, query, { t, definitions }) {
|
|
53
55
|
const normalized = query.trim().toLowerCase()
|
|
54
56
|
if (!normalized) return packages
|
|
55
|
-
return packages.filter((pkg) => packageSearchHaystack(pkg
|
|
57
|
+
return packages.filter((pkg) => packageSearchHaystack(pkg, {
|
|
58
|
+
t,
|
|
59
|
+
definition: definitions?.[pkg.slug],
|
|
60
|
+
}).includes(normalized))
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
/**
|
|
@@ -61,8 +66,10 @@ function filterPackages (packages, query) {
|
|
|
61
66
|
export function PackagesBrowser () {
|
|
62
67
|
const app = useApp()
|
|
63
68
|
const router = useRouter()
|
|
69
|
+
const { t } = useLocale()
|
|
64
70
|
const [query, setQuery] = useState('')
|
|
65
71
|
const packages = app?.manifestSummary?.packages || []
|
|
72
|
+
const definitions = app?.manifestSummary?.definitions || {}
|
|
66
73
|
const showcaseActions = useMemo(
|
|
67
74
|
() => (app?.capabilities?.tools || []).slice(0, 6).map(tool => ({
|
|
68
75
|
id: tool.actionId,
|
|
@@ -75,13 +82,14 @@ export function PackagesBrowser () {
|
|
|
75
82
|
)
|
|
76
83
|
const sorted = useMemo(
|
|
77
84
|
() => [...packages].sort((a, b) =>
|
|
78
|
-
|
|
85
|
+
resolvePackageDisplayName(a, { t, definition: definitions[a.slug] })
|
|
86
|
+
.localeCompare(resolvePackageDisplayName(b, { t, definition: definitions[b.slug] })),
|
|
79
87
|
),
|
|
80
|
-
[packages],
|
|
88
|
+
[packages, definitions, t],
|
|
81
89
|
)
|
|
82
90
|
const filtered = useMemo(
|
|
83
|
-
() => filterPackages(sorted, query),
|
|
84
|
-
[sorted, query],
|
|
91
|
+
() => filterPackages(sorted, query, { t, definitions }),
|
|
92
|
+
[sorted, query, t, definitions],
|
|
85
93
|
)
|
|
86
94
|
const totalCount = sorted.length
|
|
87
95
|
const visibleCount = filtered.length
|
|
@@ -240,7 +248,12 @@ export function PackagesBrowser () {
|
|
|
240
248
|
) : (
|
|
241
249
|
<View gap="m" layout="row-wrap">
|
|
242
250
|
{filtered.map((pkg) => (
|
|
243
|
-
<PackageStoreCard
|
|
251
|
+
<PackageStoreCard
|
|
252
|
+
key={pkg.package}
|
|
253
|
+
pkg={pkg}
|
|
254
|
+
definition={definitions[pkg.slug]}
|
|
255
|
+
router={router}
|
|
256
|
+
/>
|
|
244
257
|
))}
|
|
245
258
|
</View>
|
|
246
259
|
)}
|
|
@@ -252,11 +265,12 @@ export function PackagesBrowser () {
|
|
|
252
265
|
}
|
|
253
266
|
|
|
254
267
|
/**
|
|
255
|
-
* @param {{ pkg: object, router: ReturnType<typeof useRouter> }} props
|
|
268
|
+
* @param {{ pkg: object, definition?: object | null, router: ReturnType<typeof useRouter> }} props
|
|
256
269
|
*/
|
|
257
|
-
function PackageStoreCard ({ pkg, router }) {
|
|
258
|
-
const
|
|
259
|
-
const
|
|
270
|
+
function PackageStoreCard ({ pkg, definition, router }) {
|
|
271
|
+
const { t } = useLocale()
|
|
272
|
+
const displayName = resolvePackageDisplayName(pkg, { t, definition })
|
|
273
|
+
const pitch = resolvePackagePitch(pkg, { t })
|
|
260
274
|
const icon = getPackageIcon(pkg)
|
|
261
275
|
const actionCount = pkg.actions?.length ?? 0
|
|
262
276
|
const detailHref = router.getHref({
|
|
@@ -285,9 +299,14 @@ function PackageStoreCard ({ pkg, router }) {
|
|
|
285
299
|
{pitch}
|
|
286
300
|
</Text>
|
|
287
301
|
{actionCount > 0 && (
|
|
288
|
-
<Text
|
|
289
|
-
|
|
290
|
-
|
|
302
|
+
<Text
|
|
303
|
+
variant="small"
|
|
304
|
+
className="pkg-store-text-center pkg-store-text-subtle"
|
|
305
|
+
text={actionCount === 1
|
|
306
|
+
? 'package-catalog.card.actionOne'
|
|
307
|
+
: 'package-catalog.card.actionMany'}
|
|
308
|
+
params={{ count: actionCount }}
|
|
309
|
+
/>
|
|
291
310
|
)}
|
|
292
311
|
</View>
|
|
293
312
|
)
|
package/src/en.translations.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
+
"package-catalog.home.title": "Capabilities",
|
|
2
3
|
"package-catalog.home.hero.title": "A digital toolbox for your business",
|
|
3
4
|
"package-catalog.browser.title": "Capabilities",
|
|
4
5
|
"package-catalog.browser.description": "Browse documented, invokable capabilities for your workspace.",
|
|
@@ -32,6 +33,8 @@
|
|
|
32
33
|
"package-catalog.section.matching": "Matching capabilities",
|
|
33
34
|
"package-catalog.section.description": "Each card opens showcase and documentation — actions, widgets, and optional pages.",
|
|
34
35
|
"package-catalog.gridMeta": "Showing {visible} of {total} capabilities",
|
|
36
|
+
"package-catalog.card.actionOne": "{count} action",
|
|
37
|
+
"package-catalog.card.actionMany": "{count} actions",
|
|
35
38
|
"package-catalog.emptyManifest": "Capability catalog unavailable. Rebuild the app after upgrading @ossy/platform.",
|
|
36
39
|
"package-catalog.detail.notFound.title": "Capability not found",
|
|
37
40
|
"package-catalog.detail.notFound.body": "No manifest entries for slug: {slug}",
|
|
@@ -80,6 +83,8 @@
|
|
|
80
83
|
"package-catalog.detail.service.off": "disabled",
|
|
81
84
|
"package-catalog.detail.service.enable": "Enable service",
|
|
82
85
|
"package-catalog.detail.service.disable": "Disable service",
|
|
83
|
-
"package-catalog.detail.meta.capabilities": "{count}
|
|
86
|
+
"package-catalog.detail.meta.capabilities": "{count} capabilities included",
|
|
87
|
+
"package-catalog.detail.meta.capabilitiesOne": "{count} capability included",
|
|
88
|
+
"package-catalog.detail.meta.capabilitiesMany": "{count} capabilities included",
|
|
84
89
|
"package-catalog.detail.openAPage": "Open a page"
|
|
85
90
|
}
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { Icon, Page, ContentHeader, Text, View } from '@ossy/design-system'
|
|
2
|
+
import { Icon, Page, ContentHeader, Text, View, useLocale } from '@ossy/design-system'
|
|
3
3
|
import { useApp } from '@ossy/app/shell'
|
|
4
4
|
import { useRouter } from '@ossy/router-react'
|
|
5
5
|
import { countPackageCapabilities } from './formatPaths.js'
|
|
@@ -19,8 +19,8 @@ import { CatalogPageStyles } from './CatalogPageStyles.jsx'
|
|
|
19
19
|
import {
|
|
20
20
|
filterBackgroundTasks,
|
|
21
21
|
getPackageIcon,
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
resolvePackageDisplayName,
|
|
23
|
+
resolvePackagePitch,
|
|
24
24
|
} from './packageCopy.js'
|
|
25
25
|
|
|
26
26
|
export const metadata = {
|
|
@@ -35,6 +35,7 @@ export const metadata = {
|
|
|
35
35
|
export default function PackageDetailPage () {
|
|
36
36
|
const app = useApp()
|
|
37
37
|
const router = useRouter()
|
|
38
|
+
const { t } = useLocale()
|
|
38
39
|
const slug = router.params.packageSlug
|
|
39
40
|
const pkg = app?.manifestSummary?.packages?.find((p) => p.slug === slug)
|
|
40
41
|
|
|
@@ -47,10 +48,10 @@ export default function PackageDetailPage () {
|
|
|
47
48
|
)
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
const displayName = slugToDisplayName(pkg.slug)
|
|
51
|
-
const pitch = getPackagePitch(pkg)
|
|
52
|
-
const packageIcon = getPackageIcon(pkg)
|
|
53
51
|
const packageDefinition = app?.manifestSummary?.definitions?.[pkg.slug]
|
|
52
|
+
const displayName = resolvePackageDisplayName(pkg, { t, definition: packageDefinition })
|
|
53
|
+
const pitch = resolvePackagePitch(pkg, { t })
|
|
54
|
+
const packageIcon = getPackageIcon(pkg)
|
|
54
55
|
const totalCapabilities = countPackageCapabilities(pkg)
|
|
55
56
|
const backgroundTasks = filterBackgroundTasks(pkg.tasks)
|
|
56
57
|
|
|
@@ -75,10 +76,11 @@ export default function PackageDetailPage () {
|
|
|
75
76
|
eyebrow={pkg.package}
|
|
76
77
|
title={displayName}
|
|
77
78
|
text={pitch}
|
|
78
|
-
meta=
|
|
79
|
+
meta={totalCapabilities === 1
|
|
80
|
+
? 'package-catalog.detail.meta.capabilitiesOne'
|
|
81
|
+
: 'package-catalog.detail.meta.capabilitiesMany'}
|
|
79
82
|
metaParams={{
|
|
80
83
|
count: totalCapabilities,
|
|
81
|
-
countSuffix: totalCapabilities === 1 ? 'y' : 'ies',
|
|
82
84
|
}}
|
|
83
85
|
maxWidth="l"
|
|
84
86
|
/>
|
|
@@ -119,7 +121,7 @@ export default function PackageDetailPage () {
|
|
|
119
121
|
)}
|
|
120
122
|
|
|
121
123
|
{documentedActions.length > 0 && (
|
|
122
|
-
<PackageActionsSection
|
|
124
|
+
<PackageActionsSection actions={documentedActions} />
|
|
123
125
|
)}
|
|
124
126
|
|
|
125
127
|
{backgroundTasks.length > 0 && (
|
package/src/packageCopy.js
CHANGED
|
@@ -65,7 +65,7 @@ const PITCH_BY_SLUG = {
|
|
|
65
65
|
storage: 'Files, folders, and workspace storage',
|
|
66
66
|
tasks: 'Tasks, checklists, and async work',
|
|
67
67
|
themes: 'Themes, branding, and appearance',
|
|
68
|
-
timesheets: '
|
|
68
|
+
timesheets: 'Generate, review, save, and export monthly work hours',
|
|
69
69
|
tokens: 'Design tokens and theme variables',
|
|
70
70
|
tools: 'Developer tools and utilities',
|
|
71
71
|
users: 'Users, accounts, and membership',
|
|
@@ -107,6 +107,54 @@ export function getPackagePitch (pkg) {
|
|
|
107
107
|
return `${name} — Ossy feature package`
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Translate a key, or return null when the catalog has no entry (key echoes back).
|
|
112
|
+
*
|
|
113
|
+
* @param {(key: string, params?: Record<string, string | number>) => string} t
|
|
114
|
+
* @param {string | null | undefined} key
|
|
115
|
+
* @returns {string | null}
|
|
116
|
+
*/
|
|
117
|
+
function translateOrNull (t, key) {
|
|
118
|
+
if (!key || typeof t !== 'function') return null
|
|
119
|
+
const translated = t(key)
|
|
120
|
+
return translated && translated !== key ? translated : null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Localized package title for catalog cards / detail hero.
|
|
125
|
+
* Prefer Definition.titleKey, then `{slug}.home.title`, then English fallbacks.
|
|
126
|
+
*
|
|
127
|
+
* @param {{ slug?: string }} pkg
|
|
128
|
+
* @param {{ t?: (key: string) => string, definition?: { title?: string, titleKey?: string } | null }} [options]
|
|
129
|
+
* @returns {string}
|
|
130
|
+
*/
|
|
131
|
+
export function resolvePackageDisplayName (pkg, options = {}) {
|
|
132
|
+
const { t, definition } = options
|
|
133
|
+
const slug = pkg?.slug
|
|
134
|
+
const fromKey = translateOrNull(t, definition?.titleKey)
|
|
135
|
+
|| translateOrNull(t, slug ? `${slug}.home.title` : null)
|
|
136
|
+
if (fromKey) return fromKey
|
|
137
|
+
if (definition?.title) return definition.title
|
|
138
|
+
return slugToDisplayName(slug || '')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Localized package pitch for catalog cards / detail hero.
|
|
143
|
+
* Prefer `{slug}.catalog.pitch`, then `{slug}.home.description`, then English PITCH_BY_SLUG.
|
|
144
|
+
*
|
|
145
|
+
* @param {{ slug?: string, package?: string }} pkg
|
|
146
|
+
* @param {{ t?: (key: string) => string }} [options]
|
|
147
|
+
* @returns {string}
|
|
148
|
+
*/
|
|
149
|
+
export function resolvePackagePitch (pkg, options = {}) {
|
|
150
|
+
const { t } = options
|
|
151
|
+
const slug = pkg?.slug
|
|
152
|
+
const fromKey = translateOrNull(t, slug ? `${slug}.catalog.pitch` : null)
|
|
153
|
+
|| translateOrNull(t, slug ? `${slug}.home.description` : null)
|
|
154
|
+
if (fromKey) return fromKey
|
|
155
|
+
return getPackagePitch(pkg)
|
|
156
|
+
}
|
|
157
|
+
|
|
110
158
|
/** Known background task descriptions for package detail showcase. */
|
|
111
159
|
export const SHOWCASE_TASK_DESCRIPTIONS = {
|
|
112
160
|
'@ossy/booking/tasks/send-reminder': 'Hourly cron — emails clients ~24h before their booking.',
|
package/src/sv.translations.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
+
"package-catalog.home.title": "Funktioner",
|
|
2
3
|
"package-catalog.home.hero.title": "En digital verktygslåda för ditt företag",
|
|
3
4
|
"package-catalog.browser.title": "Funktioner",
|
|
4
5
|
"package-catalog.browser.description": "Bläddra bland dokumenterade, anropbara capabilities för din workspace.",
|
|
@@ -32,6 +33,8 @@
|
|
|
32
33
|
"package-catalog.section.matching": "Matchande funktioner",
|
|
33
34
|
"package-catalog.section.description": "Varje kort öppnar showcase och dokumentation — actions, widgets och valfria sidor.",
|
|
34
35
|
"package-catalog.gridMeta": "Visar {visible} av {total} funktioner",
|
|
36
|
+
"package-catalog.card.actionOne": "{count} action",
|
|
37
|
+
"package-catalog.card.actionMany": "{count} actions",
|
|
35
38
|
"package-catalog.emptyManifest": "Funktionskatalogen är inte tillgänglig. Bygg om appen efter uppgradering av @ossy/platform.",
|
|
36
39
|
"package-catalog.detail.notFound.title": "Funktionen hittades inte",
|
|
37
40
|
"package-catalog.detail.notFound.body": "Inga manifestposter för slug: {slug}",
|
|
@@ -80,6 +83,8 @@
|
|
|
80
83
|
"package-catalog.detail.service.off": "inaktiverad",
|
|
81
84
|
"package-catalog.detail.service.enable": "Aktivera tjänst",
|
|
82
85
|
"package-catalog.detail.service.disable": "Inaktivera tjänst",
|
|
83
|
-
"package-catalog.detail.meta.capabilities": "{count}
|
|
86
|
+
"package-catalog.detail.meta.capabilities": "{count} funktioner ingår",
|
|
87
|
+
"package-catalog.detail.meta.capabilitiesOne": "{count} funktion ingår",
|
|
88
|
+
"package-catalog.detail.meta.capabilitiesMany": "{count} funktioner ingår",
|
|
84
89
|
"package-catalog.detail.openAPage": "Öppna en sida"
|
|
85
90
|
}
|