Skip to main content
Glama
AddViewModal.vue4.79 kB
<template> <!-- NOTE: the Modal CSS for height in "max" doesn't work as we might expect --> <Modal ref="modalRef" size="lg" title="Create View"> <form @submit.prevent="nameForm.handleSubmit()"> <nameForm.Field name="name"> <template #default="{ field }"> <div v-if="field.state.meta.errors.length > 0" :class=" clsx( 'text-sm mb-xs', themeClasses('text-destructive-600', 'text-destructive-200'), ) " > {{ field.state.meta.errors[0] }} </div> </template> </nameForm.Field> <label class="flex flex-row items-center relative"> <span>View Name</span> <nameForm.Field name="name" :validators="{ onChange: viewNameValidator, onBlur: viewNameValidator, }" > <template #default="{ field }"> <input :class=" clsx( 'block w-72 ml-auto border', field.state.meta.errors.length > 0 ? themeClasses( 'text-black bg-white border-destructive-600 disabled:bg-neutral-100', 'text-white bg-black border-destructive-400 disabled:bg-neutral-900', ) : themeClasses( 'text-black bg-white border-neutral-600 disabled:bg-neutral-100', 'text-white bg-black border-neutral-400 disabled:bg-neutral-900', ), ) " :value="field.state.value" type="text" :disabled="wForm.bifrosting.value" @input=" (e) => field.handleChange((e.target as HTMLInputElement).value) " /> </template> </nameForm.Field> <Icon v-if="wForm.bifrosting.value" class="absolute right-2xs" name="loader" size="sm" tone="action" /> </label> </form> <div class="flex gap-sm mt-sm"> <NewButton label="Cancel" @click="() => modalRef?.close()" /> <nameForm.Field name="name"> <template #default="{ field }"> <NewButton class="flex-grow" icon="plus" label="Create" tone="action" :loading="wForm.bifrosting.value" :disabled=" wForm.bifrosting.value || field.state.meta.errors.length > 0 || field.state.value === '' " @click="() => nameForm.handleSubmit()" /> </template> </nameForm.Field> </div> </Modal> </template> <script setup lang="ts"> import { Modal, Icon, NewButton, themeClasses, } from "@si/vue-lib/design-system"; import { computed, ref } from "vue"; import { useRoute } from "vue-router"; import clsx from "clsx"; import { View } from "@/workers/types/entity_kind_types"; import { useApi, routes } from "./api_composables"; import { useWatchedForm } from "./logic_composables/watched_form"; const props = defineProps<{ views: View[] | undefined; }>(); const modalRef = ref<InstanceType<typeof Modal>>(); const api = useApi(); const route = useRoute(); const wForm = useWatchedForm<{ name: string }>("view.add"); const formData = computed<{ name: string }>(() => { return { name: "" }; }); const existingViewNames = computed( () => props.views?.map((view) => view.name) ?? [], ); const viewNameValidator = ({ value }: { value: string }) => { if (value.trim().length === 0) { return "Name is required"; } else if (existingViewNames.value.includes(value)) { return "That view name is already in use"; } return undefined; }; const nameForm = wForm.newForm({ data: formData, onSubmit: async ({ value }) => { const call = api.endpoint<{ id: string }>(routes.CreateView); const { req, newChangeSetId } = await call.post<{ name: string }>({ name: value.name, }); if (api.ok(req)) { modalRef.value?.close(); if (newChangeSetId) { api.navigateToNewChangeSet( { name: "new-hotness", params: { workspacePk: route.params.workspacePk, changeSetId: newChangeSetId, }, }, newChangeSetId, ); } } }, validators: { onSubmit: ({ value }) => viewNameValidator({ value: value.name }), }, watchFn: () => props.views?.length ?? 0, }); const open = () => { modalRef.value?.open(); wForm.reset(nameForm); nameForm.state.values.name = ""; }; defineExpose({ open, isOpen: modalRef.value?.isOpen }); </script>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server