Merge pull request 'fix/machine-detail-view' (#3777) from fix/machine-detail-view into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/3777
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import type { Preview } from "@kachurun/storybook-solid";
|
||||
|
||||
import "../src/index.css";
|
||||
|
||||
export const preview: Preview = {
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
|
||||
import { Button, ButtonProps } from "./Button";
|
||||
import FlashIcon from "@/icons/flash.svg";
|
||||
import Icon from "../icon";
|
||||
|
||||
const meta: Meta<ButtonProps> = {
|
||||
title: "Components/Button",
|
||||
@@ -12,12 +12,10 @@ export default meta;
|
||||
type Story = StoryObj<ButtonProps>;
|
||||
|
||||
const children = "click me";
|
||||
const startIcon = <FlashIcon width={16} height={16} viewBox="0 0 48 48" />;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children,
|
||||
startIcon,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -41,3 +39,17 @@ export const Ghost: Story = {
|
||||
variant: "ghost",
|
||||
},
|
||||
};
|
||||
|
||||
export const StartIcon: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
startIcon: <Icon size={12} icon="Flash" />,
|
||||
},
|
||||
};
|
||||
|
||||
export const EndIcon: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
endIcon: <Icon size={12} icon="Flash" />,
|
||||
},
|
||||
};
|
||||
|
||||
7
pkgs/clan-app/ui/src/components/TagList/TagList.css
Normal file
7
pkgs/clan-app/ui/src/components/TagList/TagList.css
Normal file
@@ -0,0 +1,7 @@
|
||||
div.tag-list {
|
||||
@apply flex flex-wrap gap-2;
|
||||
|
||||
span.tag {
|
||||
@apply w-fit rounded-full px-3 py-2 bg-inv-4 fg-inv-1;
|
||||
}
|
||||
}
|
||||
36
pkgs/clan-app/ui/src/components/TagList/TagList.stories.tsx
Normal file
36
pkgs/clan-app/ui/src/components/TagList/TagList.stories.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
|
||||
import { TagList, TagListProps } from "./TagList";
|
||||
|
||||
const meta: Meta<TagListProps> = {
|
||||
title: "Components/TagList",
|
||||
component: TagList,
|
||||
decorators: [
|
||||
// wrap in a fixed width div so we can check that it wraps
|
||||
(Story) => {
|
||||
return (
|
||||
<div style={{ width: "20em" }}>
|
||||
<Story />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<TagListProps>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
values: [
|
||||
"Titan",
|
||||
"Enceladus",
|
||||
"Mimas",
|
||||
"Dione",
|
||||
"Iapetus",
|
||||
"Tethys",
|
||||
"Hyperion",
|
||||
"Epimetheus",
|
||||
],
|
||||
},
|
||||
};
|
||||
21
pkgs/clan-app/ui/src/components/TagList/TagList.tsx
Normal file
21
pkgs/clan-app/ui/src/components/TagList/TagList.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Component, For } from "solid-js";
|
||||
import { Typography } from "@/src/components/Typography";
|
||||
import "./TagList.css";
|
||||
|
||||
export interface TagListProps {
|
||||
values: string[];
|
||||
}
|
||||
|
||||
export const TagList: Component<TagListProps> = (props) => {
|
||||
return (
|
||||
<div class="tag-list">
|
||||
<For each={props.values}>
|
||||
{(tag) => (
|
||||
<Typography hierarchy="label" size="s" inverted={true} class="tag">
|
||||
{tag}
|
||||
</Typography>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -137,7 +137,7 @@ export const InputLabel = (props: InputLabelProps) => {
|
||||
weight="bold"
|
||||
size="xs"
|
||||
>
|
||||
{"∗"}
|
||||
<>*</>
|
||||
</Typography>
|
||||
)}
|
||||
{props.help && (
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
FileSelectorField,
|
||||
} from "@/src/components/fileSelect";
|
||||
import { useClanContext } from "@/src/contexts/clan";
|
||||
import { TagList } from "@/src/components/TagList/TagList";
|
||||
|
||||
type MachineFormInterface = MachineData & {
|
||||
sshKey?: File;
|
||||
@@ -77,10 +78,12 @@ const LoadingBar = () => (
|
||||
function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
interface InstallMachineProps {
|
||||
name?: string;
|
||||
machine: MachineData;
|
||||
}
|
||||
|
||||
const InstallMachine = (props: InstallMachineProps) => {
|
||||
const { activeClanURI } = useClanContext();
|
||||
|
||||
@@ -381,6 +384,7 @@ const InstallMachine = (props: InstallMachineProps) => {
|
||||
interface MachineDetailsProps {
|
||||
initialData: MachineData;
|
||||
}
|
||||
|
||||
const MachineForm = (props: MachineDetailsProps) => {
|
||||
const [formStore, { Form, Field }] =
|
||||
// TODO: retrieve the correct initial values from API
|
||||
@@ -595,49 +599,47 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
</Field>
|
||||
<Field name="machine.tags" type="string[]">
|
||||
{(field, props) => (
|
||||
<div class="flex items-center gap-4">
|
||||
<Typography hierarchy="label" size="default" weight="bold">
|
||||
<div class="grid grid-cols-10 items-center">
|
||||
<Typography
|
||||
hierarchy="label"
|
||||
size="default"
|
||||
weight="bold"
|
||||
class="col-span-5"
|
||||
>
|
||||
Tags{" "}
|
||||
</Typography>
|
||||
<For each={field.value}>
|
||||
{(tag) => (
|
||||
<span class="mx-2 w-fit rounded-full px-3 py-0.5 bg-inv-4 fg-inv-1">
|
||||
<Typography
|
||||
hierarchy="label"
|
||||
size="s"
|
||||
inverted={true}
|
||||
>
|
||||
{tag}
|
||||
</Typography>
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
<div class="col-span-5 justify-self-end">
|
||||
{/* alphabetically sort the tags */}
|
||||
<TagList values={[...(field.value || [])].sort()} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
</Fieldset>
|
||||
|
||||
<Fieldset legend="Hardware">
|
||||
<Field name="hw_config">
|
||||
{(field, props) => (
|
||||
<FieldLayout
|
||||
label={<InputLabel>Hardware Configuration</InputLabel>}
|
||||
field={<span>{field.value || "None"}</span>}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<hr />
|
||||
<Field name="disk_schema.schema_name">
|
||||
{(field, props) => (
|
||||
<>
|
||||
<Typography hierarchy={"body"} size={"s"}>
|
||||
<Fieldset legend="Hardware">
|
||||
<Field name="hw_config">
|
||||
{(field, props) => (
|
||||
<FieldLayout
|
||||
label={<InputLabel>Disk schema</InputLabel>}
|
||||
label={<InputLabel>Hardware Configuration</InputLabel>}
|
||||
field={<span>{field.value || "None"}</span>}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</Fieldset>
|
||||
)}
|
||||
</Field>
|
||||
<hr />
|
||||
<Field name="disk_schema.schema_name">
|
||||
{(field, props) => (
|
||||
<>
|
||||
<FieldLayout
|
||||
label={<InputLabel>Disk schema</InputLabel>}
|
||||
field={<span>{field.value || "None"}</span>}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</Fieldset>
|
||||
</Typography>
|
||||
|
||||
<Accordion title="Connection Settings">
|
||||
<Fieldset>
|
||||
|
||||
Reference in New Issue
Block a user