Skip to content

Commit 9bc7d11

Browse files
committed
added in app route
1 parent e891e30 commit 9bc7d11

File tree

23 files changed

+586
-75
lines changed

23 files changed

+586
-75
lines changed

Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ RUN mkdir /app
99
COPY . /app
1010

1111
WORKDIR /app
12+
# csrf check is disabled for local development
13+
RUN rm svelte.config.js
14+
# csrf check is enabled for prod
15+
RUN mv svelte.config.prod.js svelte.config.js
1216
RUN npm install
1317
RUN npm run build
1418

spec/yizy-spec.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
{
9898
"name": "getSpecs",
9999
"description": "gets a list of spec that belongs to the user",
100-
"url": "/api/specs/getSpecs",
100+
"url": "/api/spec/getSpecs",
101101
"requestModel": {
102102
"name": "GetSpecsRequest",
103103
"fields": [
@@ -132,7 +132,7 @@
132132
{
133133
"name": "getLatestSpecById",
134134
"description": "gets the latest snapshot of a spec by id",
135-
"url": "/api/specs/getLatestSpecById",
135+
"url": "/api/spec/getLatestSpecById",
136136
"requestModel": {
137137
"name": "GetLatestSpecByIdRequest",
138138
"fields": [

src/app.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
}
4242
body {
4343
@apply bg-background text-foreground;
44+
margin: 0;
45+
height: 100%;
46+
overflow: hidden;
4447
}
4548
}
4649

src/lib/api-client/yizyClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export async function getSpecs(
128128
defaultHeaders = { ...defaultHeaders, ...headers };
129129
}
130130

131-
const response = await fetch("http://localhost:5173/api/specs/getSpecs", {
131+
const response = await fetch("http://localhost:5173/api/spec/getSpecs", {
132132
method: "POST",
133133
headers: defaultHeaders,
134134
body: JSON.stringify(req),
@@ -151,7 +151,7 @@ export async function getLatestSpecById(
151151
}
152152

153153
const response = await fetch(
154-
"http://localhost:5173/api/specs/getLatestSpecById",
154+
"http://localhost:5173/api/spec/getLatestSpecById",
155155
{
156156
method: "POST",
157157
headers: defaultHeaders,

src/lib/components/ui/editor/YizyEditor.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191
});
9292
</script>
9393

94-
<div class="w-full p-6 pb-20">
94+
<div class="w-full pb-20">
9595
<div class="mx-auto grid w-full grid-cols-1 rounded-lg lg:grid-cols-5 lg:gap-2">
9696
<div class="col-span-1 rounded-lg px-6 pr-10 pt-4 lg:col-span-3 lg:pb-4">
9797
<div class="mt-4 border-muted lg:mb-2">

src/lib/components/ui/editor/models/models.ts

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,3 @@
1-
//import {
2-
// type ArrayType,
3-
// arrayType,
4-
// type DataType,
5-
// type Field as YizyField,
6-
// type FieldDataType,
7-
// isPrimitiveType,
8-
// type NullableArrayType,
9-
// nullableArrayType,
10-
// type NullableReferenceType,
11-
// nullableReferenceType,
12-
// type ObjectType,
13-
// objectType,
14-
// type PrimitiveTypes,
15-
// type ReferenceType,
16-
// type Service,
17-
// TypeIdentifier,
18-
//} from "@yizy/spec";
191
import { ProgrammingLanguage } from "$lib/models/constants";
202
import * as yizy from "@yizy/spec";
213

@@ -27,6 +9,53 @@ export interface Document {
279
additionalModels: Model[];
2810
}
2911

12+
export const DEFAULT_DOCUMENT: Document = {
13+
name: "",
14+
description: "",
15+
environment: [
16+
{
17+
name: "",
18+
baseUrl: "",
19+
},
20+
],
21+
endpoints: [
22+
{
23+
name: "",
24+
url: "",
25+
description: "",
26+
req: {
27+
name: "",
28+
fields: [
29+
{
30+
name: "",
31+
type: "",
32+
},
33+
],
34+
},
35+
res: {
36+
name: "",
37+
fields: [
38+
{
39+
name: "",
40+
type: "",
41+
},
42+
],
43+
},
44+
},
45+
],
46+
additionalModels: [
47+
{
48+
name: "",
49+
fields: [
50+
{
51+
name: "",
52+
type: "",
53+
},
54+
],
55+
},
56+
],
57+
};
58+
3059
export interface Environment {
3160
name: string;
3261
baseUrl: string;
@@ -128,7 +157,7 @@ export function docToYizySpec(doc: Document): yizy.Service {
128157
return service;
129158
}
130159

131-
function specTypeToNativeType(dataType: yizy.Datatype): string {
160+
function specTypeToNativeType(dataType: yizy.DataType): string {
132161
const typeMap = {
133162
float: "float",
134163
"float?": "float?",
@@ -165,7 +194,7 @@ function specTypeToNativeType(dataType: yizy.Datatype): string {
165194
return typeof (dataType as yizy.NullableObjectType).name === "string"
166195
? (((dataType as yizy.NullableObjectType).name + "?") as string)
167196
: yizy.getLanguageSpecificName(
168-
(dataType as yizy.NullableObjectType).name as NameMap,
197+
(dataType as yizy.NullableObjectType).name as yizy.NameMap,
169198
ProgrammingLanguage.Typescript,
170199
) + "?";
171200
case yizy.TypeIdentifier.ReferenceType:

src/routes/(app)/app/+page.svelte

Lines changed: 176 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,121 @@
11
<script lang="ts">
2-
import { Files, User, Plus } from 'lucide-svelte';
3-
2+
import * as Tabs from '$lib/components/ui/tabs';
3+
import { Files, User, Plus, LoaderCircle } from 'lucide-svelte';
44
import YizyLogo from '$lib/components/ui/YIZYLogo.svelte';
5+
import * as yizy from '$lib/api-client/yizyClient';
6+
import { onMount } from 'svelte';
7+
import { Button } from '$lib/components/ui/button/index.js';
8+
import * as Dialog from '$lib/components/ui/dialog/index.js';
9+
import { Input } from '$lib/components/ui/input/index.js';
10+
import { Label } from '$lib/components/ui/label/index.js';
11+
import { currentService } from '$lib/state';
12+
import { yizySpecToDoc } from '$lib/components/ui/editor/models/models';
13+
import * as yizySpec from '@yizy/spec';
14+
import CodeTab from './components/CodeTab.svelte';
15+
import SpecTab from './components/SpecTab.svelte';
16+
import { DEFAULT_DOCUMENT } from '$lib/components/ui/editor/models/models';
17+
18+
let { data } = $props();
19+
20+
interface SpecDto {
21+
id: string;
22+
name: string;
23+
}
24+
25+
interface SpecDetailDto {
26+
id: string;
27+
snapshotId: string;
28+
}
29+
30+
let doc = $state(yizySpecToDoc($currentService));
31+
let specs: SpecDto[] = $state([]);
32+
let selectedSpec: SpecDto | null = $state(null);
33+
let selectedSpecDetails: SpecDetailDto | null = $state(null);
34+
let specContent: string = $state('');
35+
let isContentLoading: boolean = $state(false);
36+
let createSpecDialogSpecName: string = $state('');
37+
let isCreateSpecDialogOpen: boolean = $state(false);
38+
39+
function closeCreateSpecDialog() {
40+
isCreateSpecDialogOpen = false;
41+
}
42+
43+
function isSelectedSpec(spec: SpecDto) {
44+
if (!selectedSpec) return false;
45+
return spec.id == selectedSpec.id;
46+
}
47+
48+
async function selectSpecClicked(spec: SpecDto) {
49+
selectedSpec = spec;
50+
isContentLoading = true;
51+
const res = await yizy.getLatestSpecById({ id: spec.id });
52+
if (res.error === null && res.result !== null) {
53+
selectedSpecDetails = {
54+
id: spec.id,
55+
snapshotId: res.result.snapshotId
56+
};
57+
specContent = res.result?.content;
58+
try {
59+
const service = JSON.parse(specContent) as yizySpec.Service;
60+
doc = yizySpecToDoc(service);
61+
} catch (e) {
62+
console.log('cannot parse spec!');
63+
doc = DEFAULT_DOCUMENT;
64+
}
65+
}
66+
isContentLoading = false;
67+
}
68+
69+
async function createSpecDialogSaveBtnClicked() {
70+
if (data.authState) {
71+
const res = await yizy.createSpec({
72+
name: createSpecDialogSpecName,
73+
creatorUserId: data.authState?.user.uuid
74+
});
75+
if (res.error === null && res.result != null) {
76+
specs = specs.concat(res.result);
77+
closeCreateSpecDialog();
78+
}
79+
} else {
80+
console.log('please login!');
81+
}
82+
}
83+
84+
async function updateSpecBtnClicked(specContent: string) {
85+
if (data.authState && selectedSpecDetails) {
86+
const res = await yizy.updateSpec({
87+
specId: selectedSpecDetails.id,
88+
content: specContent,
89+
updatorUserId: data.authState.user.uuid,
90+
prevSpecSnapshotId: selectedSpecDetails.snapshotId
91+
});
92+
if (res.error == null && res.result != null) {
93+
selectedSpecDetails = {
94+
snapshotId: res.result?.prevSnapshotId,
95+
id: selectedSpecDetails.id
96+
};
97+
}
98+
return;
99+
} else {
100+
console.log('please login!');
101+
}
102+
}
103+
104+
onMount(async () => {
105+
if (data.authState) {
106+
const res = await yizy.getSpecs({
107+
userId: data.authState?.user.uuid
108+
});
109+
if (res.error == null) {
110+
specs = res.result.resultset;
111+
}
112+
}
113+
});
5114
</script>
6115

7-
<div class="flex h-full flex-row">
8-
<div class="flex h-full w-1/5 flex-col border-r">
9-
<div class="flex h-full flex-row">
116+
<div class="flex h-screen flex-row">
117+
<div class="flex h-screen w-1/5 flex-col border-r">
118+
<div class="flex h-full w-full flex-row">
10119
<div class="flex h-full w-12 flex-col gap-3 bg-secondary px-2 py-4">
11120
<button class="w-full"><Files class="h-8 w-8" /></button>
12121
<button class="w-full"><User class="h-8 w-8 text-muted" /></button>
@@ -16,13 +125,71 @@
16125
<YizyLogo />
17126
</div>
18127
<div class="flex flex-row justify-between bg-border px-2 py-1">
19-
<div class="my-auto text-sm">My Specs</div>
20-
<button class="my-auto h-5"><Plus class="h-full" /></button>
128+
<div class="my-auto text-sm font-bold">My Specs</div>
129+
<Dialog.Root bind:open={isCreateSpecDialogOpen}>
130+
<Dialog.Trigger class="my-auto flex">
131+
<Plus class="h-5" />
132+
</Dialog.Trigger>
133+
<Dialog.Content class="sm:max-w-[425px]">
134+
<Dialog.Header>
135+
<Dialog.Title>Create New API Spec</Dialog.Title>
136+
<Dialog.Description>Create a new API spec</Dialog.Description>
137+
</Dialog.Header>
138+
<div class="grid gap-4 py-4">
139+
<div class="grid grid-cols-4 items-center gap-4">
140+
<Label for="name" class="text-right">Name</Label>
141+
<Input
142+
id="name"
143+
placeholder="My API Service"
144+
class="col-span-3"
145+
bind:value={createSpecDialogSpecName} />
146+
</div>
147+
</div>
148+
<Dialog.Footer>
149+
<Button
150+
onclick={async () => {
151+
await createSpecDialogSaveBtnClicked();
152+
}}>Create</Button>
153+
</Dialog.Footer>
154+
</Dialog.Content>
155+
</Dialog.Root>
21156
</div>
157+
{#each specs as s}
158+
{#if isSelectedSpec(s)}
159+
<button class="bg-primary px-2 py-1 text-left text-sm text-primary-foreground"
160+
>{s.name}</button>
161+
{:else}
162+
<button
163+
class="px-2 py-1 text-left text-sm hover:bg-primary hover:text-primary-foreground"
164+
onclick={async () => {
165+
selectSpecClicked(s);
166+
}}>{s.name}</button>
167+
{/if}
168+
{/each}
22169
</div>
23170
</div>
24171
</div>
25-
<div class="flex w-4/5 p-2">
26-
<div class="m-auto">Create a new spec</div>
172+
173+
<div class="flex h-full w-4/5 flex-col overflow-y-scroll">
174+
{#if isContentLoading}
175+
<div class="m-auto">
176+
<LoaderCircle class="animate-spin text-primary" />
177+
</div>
178+
{:else if selectedSpec === null}
179+
<div class="m-auto">Select a spec or Create a new spec</div>
180+
{:else}
181+
<Tabs.Root value="api-spec" class="w-full p-4">
182+
<Tabs.List class="grid w-full grid-cols-2">
183+
<Tabs.Trigger value="api-spec">Edit Spec</Tabs.Trigger>
184+
<Tabs.Trigger value="code-gen">Generate Code</Tabs.Trigger>
185+
</Tabs.List>
186+
<Tabs.Content value="api-spec">
187+
<SpecTab bind:doc onGenerateBtnClicked={updateSpecBtnClicked}></SpecTab>
188+
</Tabs.Content>
189+
<Tabs.Content value="code-gen">
190+
<CodeTab bind:doc />
191+
</Tabs.Content>
192+
</Tabs.Root>
193+
{/if}
27194
</div>
28195
</div>

0 commit comments

Comments
 (0)