Kaynağa Gözat

feat: add property table component

Thomas Zhang 3 ay önce
ebeveyn
işleme
61349b01b6

+ 4 - 0
src/lib/components/common/property-table/index.ts

@@ -0,0 +1,4 @@
+import PropertyTable from './table.svelte';
+
+export { PropertyTable };
+export { toAuthProps, toDeploymentProps } from './types';

+ 43 - 0
src/lib/components/common/property-table/table.svelte

@@ -0,0 +1,43 @@
+<script lang="ts">
+	import type { Snippet } from 'svelte';
+
+	let {
+		class: className = '',
+		properties,
+	}: {
+		class?: string;
+		properties: Record<string, string | number | string[] | Snippet>;
+	} = $props();
+
+	const entries = $derived(
+		Object.entries(properties).filter(([, v]) => {
+			if (Array.isArray(v)) return v.length > 0;
+			return v !== '' && v !== undefined && v !== null;
+		}),
+	);
+</script>
+
+<dl class={['space-y-2', className]}>
+	{#each entries as [label, value] (label)}
+		<div class="flex items-center gap-4">
+			<div class="w-32 shrink-0 text-right text-sm text-white/35">{label}</div>
+			{#if typeof value === 'function'}
+				{@render value()}
+			{:else if Array.isArray(value)}
+				<div class="flex flex-wrap gap-1">
+					{#each value as item (item)}
+						<span class="rounded bg-white/10 px-1 text-xs text-white/60">
+							{item}
+						</span>
+					{/each}
+				</div>
+			{:else}
+				<span
+					class={['text-sm break-all text-white/75', typeof value === 'number' ? 'font-mono' : '']}
+				>
+					{value}
+				</span>
+			{/if}
+		</div>
+	{/each}
+</dl>

+ 62 - 0
src/lib/components/common/property-table/types.ts

@@ -0,0 +1,62 @@
+import type { AuthConfig, RuntimeTemplate } from '$lib/types/entities';
+
+interface Entity {
+	id: string;
+	name: string;
+	icon?: string;
+	description: string;
+	tags: string[];
+}
+
+const toInfoProps = (entity: Entity) => {
+	const props: Record<string, string | number | string[]> = {};
+	props['ID'] = entity.id;
+	props['Description'] =
+		entity.description && entity.description.length > 0 ? entity.description : '<no description>';
+	props['Tags'] = entity.tags && entity.tags.length > 0 ? entity.tags : '<no tags>';
+	return props;
+};
+
+const toAuthProps = (auth: AuthConfig) => {
+	const props: Record<string, string> = {};
+	if (auth.type === 'none') {
+		return { 'Auth Type': 'None' };
+	}
+	if (auth.type === 'api-key') {
+		props['Auth Type'] = 'API Key';
+		props['API Key Header'] = auth.headerName;
+	} else if (auth.type === 'bearer') {
+		props['Auth Type'] = 'Bearer Token';
+	}
+	if (auth.type === 'api-key' || auth.type === 'bearer') {
+		props['Secret'] = `${auth.secretName}/${auth.secretKey}`;
+	}
+	if (auth.headers && auth.headers.length > 0) {
+		props['Custom Headers'] = auth.headers
+			? auth.headers.map((h) => `${h.name}: ${h.value}`).join('; ')
+			: 'None';
+	}
+	return props;
+};
+
+const toDeploymentProps = (deployment: RuntimeTemplate) => {
+	const props: Record<string, string | number | string[]> = {};
+	if (deployment.name) props['Runtime'] = deployment.name;
+	if (deployment.version) props['Runtime Version'] = deployment.version;
+	if (deployment.replicas !== undefined) props['Replicas'] = deployment.replicas;
+	if (deployment.image) props['Image'] = deployment.image;
+	if (deployment.command) props['Command'] = deployment.command;
+	if (deployment.args) props['Args'] = deployment.args;
+	if (deployment.extraArgs) props['Extra Args'] = deployment.extraArgs;
+	if (deployment.env)
+		props['Environment Variables'] = deployment.env.map((e) => `${e.name}=${e.value}`);
+	if (deployment.port !== undefined) props['Port'] = deployment.port;
+	if (deployment.cpu) props['CPU'] = deployment.cpu;
+	if (deployment.memory) props['Memory'] = deployment.memory;
+	if (deployment.node && deployment.node['locostack.com/gpu-model'])
+		props['GPU Model'] = deployment.node['locostack.com/gpu-model'];
+	if (deployment.gpu !== undefined) props['GPU'] = deployment.gpu;
+	return props;
+};
+
+export { toAuthProps, toDeploymentProps, toInfoProps };