feat(frontend): add stat card (#2687)

* feat(frontend): add stat card

* feat(frontend): fix layout
This commit is contained in:
Faton Ramadani
2023-11-24 20:17:45 +01:00
committed by GitHub
parent aeb8ee4d7b
commit 4a768c9d6b
5 changed files with 139 additions and 3 deletions
@@ -0,0 +1,92 @@
<script lang="ts">
import { getContext } from 'svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
import { initCss } from '../../utils'
import { components } from '../../editor/component'
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
import { ArrowDown } from 'lucide-svelte'
import { twMerge } from 'tailwind-merge'
import { loadIcon } from '../icon'
export let id: string
export let configuration: RichConfigurations
export let customCss: ComponentCustomCSS<'statcomponent'> | undefined = undefined
export let render: boolean
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['statcomponent'].initialData.configuration,
configuration
)
initOutput($worldStore, id, {})
let css = initCss($app.css?.statcomponent, customCss)
let iconComponent: any
$: resolvedConfig.icon && handleIcon()
async function handleIcon() {
if (resolvedConfig.icon) {
iconComponent = await loadIcon(resolvedConfig.icon)
}
}
</script>
{#each Object.keys(components['statcomponent'].initialData.configuration) as key (key)}
<ResolveConfig
{id}
{key}
bind:resolvedConfig={resolvedConfig[key]}
configuration={configuration[key]}
/>
{/each}
{#each Object.keys(css ?? {}) as key (key)}
<ResolveStyle
{id}
{customCss}
{key}
bind:css={css[key]}
componentStyle={$app.css?.imagecomponent}
/>
{/each}
{#if render}
<div class="p-6 border rounded-md h-full flex flex-row gap-6 w-full items-center">
{#if resolvedConfig.icon && iconComponent}
<div class="bg-blue-500 rounded-md p-2">
<svelte:component this={iconComponent} size={24} color="white" />
</div>
{/if}
<div class="w-full">
<div class="text-base font-normal text-primary">{resolvedConfig.title}</div>
<div class="mt-1 flex items-baseline justify-between">
<div class="flex items-baseline text-2xl font-semibold text-blue-600 dark:text-blue-200">
{resolvedConfig?.value}
</div>
{#if resolvedConfig?.progress !== undefined && resolvedConfig?.progress !== null && resolvedConfig?.progress !== 0}
<div
class={twMerge(
'flex items-center flex-row gap-2 rounded-full px-2.5 py-0.5 text-sm font-medium',
resolvedConfig?.progress > 0
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
)}
>
<ArrowDown
size={16}
class={resolvedConfig?.progress > 0 ? 'transform rotate-180' : 'transform rotate-0'}
/>
{resolvedConfig?.progress}%
</div>
{/if}
</div>
</div>
</div>
{/if}
@@ -63,6 +63,7 @@
import AppCarouselList from '../../components/display/AppCarouselList.svelte'
import AppAggridTableEe from '../../components/display/table/AppAggridTableEe.svelte'
import AppCustomComponent from '../../components/display/AppCustomComponent.svelte'
import AppStatCard from '../../components/display/AppStatCard.svelte'
export let component: AppComponent
export let selected: boolean
@@ -677,6 +678,13 @@
{render}
bind:initializing
/>
{:else if component.type === 'statcomponent'}
<AppStatCard
id={component.id}
configuration={component.configuration}
customCss={component.customCss}
{render}
/>
{/if}
</div>
</div>
@@ -40,7 +40,8 @@ import {
PanelLeft,
PanelTopInactive,
ListIcon,
Heading1
Heading1,
FileBarChart
} from 'lucide-svelte'
import type {
Aligned,
@@ -162,6 +163,7 @@ export type SelectTabComponent = BaseComponent<'selecttabcomponent'>
export type SelectStepComponent = BaseComponent<'selectstepcomponent'>
export type CarouselListComponent = BaseComponent<'carousellistcomponent'>
export type StatisticCardComponent = BaseComponent<'statcomponent'>
export type TypedComponent =
| DisplayComponent
@@ -223,6 +225,7 @@ export type TypedComponent =
| CarouselListComponent
| PlotlyComponentV2
| ChartJsComponentV2
| StatisticCardComponent
export type AppComponent = BaseAppComponent & TypedComponent
@@ -2712,6 +2715,37 @@ This is a paragraph.
}
] as AppInputSpec<'boolean', boolean>[]
}
},
statcomponent: {
name: 'Statistic card',
icon: FileBarChart,
documentationLink: `${documentationBaseUrl}/statistic_card`,
dims: '2:4-3:4' as AppComponentDimensions,
customCss: {},
initialData: {
configuration: {
title: {
type: 'static',
value: 'Title',
fieldType: 'text'
},
value: {
type: 'static',
value: 'Value',
fieldType: 'text'
},
progress: {
type: 'static',
value: 0,
fieldType: 'number'
},
icon: {
type: 'static',
value: undefined,
fieldType: 'icon-select'
}
}
}
}
} as const
@@ -64,7 +64,8 @@ const display: ComponentSet = {
'displaycomponent',
'jobidlogcomponent',
'jobidflowstatuscomponent',
'customcomponent'
'customcomponent',
'statcomponent'
]
} as const
@@ -744,5 +744,6 @@ export const quickStyleProperties: Record<
allTabs: [typographyGrouping, sizeGrouping],
selectedTab: [typographyGrouping, sizeGrouping]
},
selectstepcomponent: {}
selectstepcomponent: {},
statcomponent: {}
}