diff --git a/app/src/components/globals/skeletons/AppCatalogSkeleton.vue b/app/src/components/globals/skeletons/AppCatalogSkeleton.vue new file mode 100644 index 00000000..06792016 --- /dev/null +++ b/app/src/components/globals/skeletons/AppCatalogSkeleton.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/app/src/types/core/api.ts b/app/src/types/core/api.ts new file mode 100644 index 00000000..9fe94c96 --- /dev/null +++ b/app/src/types/core/api.ts @@ -0,0 +1,73 @@ +import type { Obj, Translation } from '@/types/commons' + +// APPS + +export type AppLevel = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 +export type AppState = 'working' | 'inprogress' | 'thirdparty' +export type AppUpstream = { + license?: string | null + website?: string | null + demo?: string | null + admindoc?: string | null + userdoc?: string | null + code?: string | null + cpe?: string | null +} +export type AppMinManifest = { + packaging_format: 1 | 2 + id: string + name: string + description: Translation + version: string + maintainers: string[] + integration: { + architectures: string | string[] + ldap: 'not_relevant' | boolean + sso: 'not_relevant' | boolean + multi_instance: boolean + disk: string + ram: { build: string; runtime: string } + yunohost: string + } + upstream: AppUpstream +} +type CatalogApp = { + added_in_catalog: number + antifeatures: string[] + category: string + featured: boolean + git: { + branch: string + revision: string + url: string | null + } | null + high_quality: boolean + id: string + lastUpdate: number + level: AppLevel + logo_hash: string | null + maintained: boolean + manifest: AppMinManifest + potential_alternative_to: string[] + state: AppState + subtags: string[] + repository: string + installed: boolean +} + +export type Catalog = { + apps: Obj + categories: { + description: string + icon: string + id: string + subtags: { id: string; title: string }[] + title: string + }[] + antifeatures: { + description: string + icon: string + id: string + title: string + }[] +} diff --git a/app/src/views/app/AppCatalog.vue b/app/src/views/app/AppCatalog.vue index e8b48fe6..a8fcd016 100644 --- a/app/src/views/app/AppCatalog.vue +++ b/app/src/views/app/AppCatalog.vue @@ -1,28 +1,29 @@ - - - @@ -449,13 +374,9 @@ const onCustomInstallClick = onSubmit(async () => { } .card-deck { - padding: 0; - margin-bottom: 0; - display: flex; - flex-flow: row wrap; - - > * { + .card { flex-basis: 100%; + outline: none; @include media-breakpoint-up(md) { flex-basis: 50%; @@ -466,14 +387,15 @@ const onCustomInstallClick = onSubmit(async () => { flex-basis: 33%; max-width: calc(33.3% - 1rem); } - } - .card { &:hover { color: $white; background-color: $dark; border-color: $dark; } + &:focus { + box-shadow: 0 0 0 $btn-focus-width rgba($dark, 0.5); + } :deep(.card-link) { color: inherit; diff --git a/app/src/views/app/appData.ts b/app/src/views/app/appData.ts new file mode 100644 index 00000000..15f275b3 --- /dev/null +++ b/app/src/views/app/appData.ts @@ -0,0 +1,19 @@ +import type { AppLevel, AppState } from '@/types/core/api' + +export function formatAppQuality(app: { state: AppState; level: AppLevel }) { + const variants = { + working: 'success', + lowquality: 'warning', + inprogress: 'danger', + broken: 'danger', + thirdparty: 'danger', + } as const + const working = app.state === 'working' + const state: keyof typeof variants = + working && app.level <= 0 + ? 'broken' + : working && app.level <= 4 + ? 'lowquality' + : app.state + return { state, variant: variants[state] } +}