aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/ui/src/models/instances.ts
diff options
context:
space:
mode:
author苏向夜 <46275354+fu050409@users.noreply.github.com>2026-02-25 02:06:07 +0800
committerGitHub <noreply@github.com>2026-02-25 02:06:07 +0800
commit78ac61904d78d558d092eff08c9f261cbdb187e8 (patch)
tree96f68d1f1554ee3a0532793afaaa52b0c73dcbec /packages/ui/src/models/instances.ts
parent8ff3af6cb908fd824b512379dd21ed4f595ab6bb (diff)
parent329734b23957b84cde2af459fa61c7385fb5b5f1 (diff)
downloadDropOut-78ac61904d78d558d092eff08c9f261cbdb187e8.tar.gz
DropOut-78ac61904d78d558d092eff08c9f261cbdb187e8.zip
feat(ui): partial react rewrite (#77)
## Summary by Sourcery Export backend data structures to TypeScript for the new React-based UI and update CI to build additional targets. New Features: - Generate TypeScript definitions for core backend structs and enums used by the UI. - Now use our own Azure app(_DropOut_) to finish the authorize process. Enhancements: - Annotate existing Rust models with ts-rs metadata to control exported TypeScript shapes, including tagged enums and opaque JSON fields. Build: - Add ts-rs as a dependency for generating TypeScript bindings from Rust types. CI: - Extend the Semifold CI workflow to run on the dev branch and build additional Linux musl and Windows GNU targets using cross where needed.
Diffstat (limited to 'packages/ui/src/models/instances.ts')
-rw-r--r--packages/ui/src/models/instances.ts135
1 files changed, 135 insertions, 0 deletions
diff --git a/packages/ui/src/models/instances.ts b/packages/ui/src/models/instances.ts
new file mode 100644
index 0000000..f434c7c
--- /dev/null
+++ b/packages/ui/src/models/instances.ts
@@ -0,0 +1,135 @@
+import { toast } from "sonner";
+import { create } from "zustand";
+import {
+ createInstance,
+ deleteInstance,
+ duplicateInstance,
+ getActiveInstance,
+ getInstance,
+ listInstances,
+ setActiveInstance,
+ updateInstance,
+} from "@/client";
+import type { Instance } from "@/types";
+
+interface InstancesState {
+ // State
+ instances: Instance[];
+ activeInstance: Instance | null;
+
+ // Actions
+ refresh: () => Promise<void>;
+ create: (name: string) => Promise<Instance | null>;
+ delete: (id: string) => Promise<void>;
+ update: (instance: Instance) => Promise<void>;
+ setActiveInstance: (instance: Instance) => Promise<void>;
+ duplicate: (id: string, newName: string) => Promise<Instance | null>;
+ getInstance: (id: string) => Promise<Instance | null>;
+}
+
+export const useInstancesStore = create<InstancesState>((set, get) => ({
+ // Initial state
+ instances: [],
+ activeInstance: null,
+
+ // Actions
+ refresh: async () => {
+ const { setActiveInstance } = get();
+ try {
+ const instances = await listInstances();
+ const active = await getActiveInstance();
+
+ if (!active && instances.length > 0) {
+ // If no active instance but instances exist, set the first one as active
+ await setActiveInstance(instances[0]);
+ }
+
+ set({ instances });
+ } catch (e) {
+ console.error("Failed to load instances:", e);
+ toast.error("Error loading instances");
+ }
+ },
+
+ create: async (name) => {
+ const { refresh } = get();
+ try {
+ const instance = await createInstance(name);
+ await refresh();
+ toast.success(`Instance "${name}" created successfully`);
+ return instance;
+ } catch (e) {
+ console.error("Failed to create instance:", e);
+ toast.error("Error creating instance");
+ return null;
+ }
+ },
+
+ delete: async (id) => {
+ const { refresh, instances, activeInstance, setActiveInstance } = get();
+ try {
+ await deleteInstance(id);
+ await refresh();
+
+ // If deleted instance was active, set another as active
+ if (activeInstance?.id === id) {
+ if (instances.length > 0) {
+ await setActiveInstance(instances[0]);
+ } else {
+ set({ activeInstance: null });
+ }
+ }
+
+ toast.success("Instance deleted successfully");
+ } catch (e) {
+ console.error("Failed to delete instance:", e);
+ toast.error("Error deleting instance");
+ }
+ },
+
+ update: async (instance) => {
+ const { refresh } = get();
+ try {
+ await updateInstance(instance);
+ await refresh();
+ toast.success("Instance updated successfully");
+ } catch (e) {
+ console.error("Failed to update instance:", e);
+ toast.error("Error updating instance");
+ }
+ },
+
+ setActiveInstance: async (instance) => {
+ try {
+ await setActiveInstance(instance.id);
+ set({ activeInstance: instance });
+ toast.success("Active instance changed");
+ } catch (e) {
+ console.error("Failed to set active instance:", e);
+ toast.error("Error setting active instance");
+ }
+ },
+
+ duplicate: async (id, newName) => {
+ const { refresh } = get();
+ try {
+ const instance = await duplicateInstance(id, newName);
+ await refresh();
+ toast.success(`Instance duplicated as "${newName}"`);
+ return instance;
+ } catch (e) {
+ console.error("Failed to duplicate instance:", e);
+ toast.error("Error duplicating instance");
+ return null;
+ }
+ },
+
+ getInstance: async (id) => {
+ try {
+ return await getInstance(id);
+ } catch (e) {
+ console.error("Failed to get instance:", e);
+ return null;
+ }
+ },
+}));