diff --git a/src/assets/icons/error.svg b/src/assets/icons/error.svg new file mode 100644 index 0000000..d9ea3d0 --- /dev/null +++ b/src/assets/icons/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/CommandPalette.vue b/src/components/CommandPalette.vue index e9f1a1a..e89b7be 100644 --- a/src/components/CommandPalette.vue +++ b/src/components/CommandPalette.vue @@ -2,6 +2,8 @@ import { useMagicKeys } from "@vueuse/core"; import { injectStrict } from "@/lib/utils"; import Loading from "@/assets/icons/loading.svg"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import Error from "@/assets/icons/Error.svg"; import { CommandPaletteStateKey, @@ -23,7 +25,7 @@ import { const keys = useMagicKeys(); const cmdK = keys["Cmd+K"]; -const { open, commands, callStack, loading } = injectStrict( +const { open, commands, callStack, loading, executionError } = injectStrict( CommandPaletteStateKey ); const openCommandPalette = injectStrict(OpenCommandPaletteKey); @@ -100,6 +102,17 @@ watchEffect(() => { + + + Something went wrong! + {{ + executionError + }} + diff --git a/src/components/tables/configmaps/columns.ts b/src/components/tables/configmaps.ts similarity index 100% rename from src/components/tables/configmaps/columns.ts rename to src/components/tables/configmaps.ts diff --git a/src/components/tables/cronjobs/columns.ts b/src/components/tables/cronjobs.ts similarity index 100% rename from src/components/tables/cronjobs/columns.ts rename to src/components/tables/cronjobs.ts diff --git a/src/components/tables/deployments/columns.ts b/src/components/tables/deployments.ts similarity index 100% rename from src/components/tables/deployments/columns.ts rename to src/components/tables/deployments.ts diff --git a/src/components/tables/ingresses/columns.ts b/src/components/tables/ingresses.ts similarity index 100% rename from src/components/tables/ingresses/columns.ts rename to src/components/tables/ingresses.ts diff --git a/src/components/tables/jobs/columns.ts b/src/components/tables/jobs.ts similarity index 100% rename from src/components/tables/jobs/columns.ts rename to src/components/tables/jobs.ts diff --git a/src/components/tables/persistentvolumeclaims/columns.ts b/src/components/tables/persistentvolumeclaims.ts similarity index 100% rename from src/components/tables/persistentvolumeclaims/columns.ts rename to src/components/tables/persistentvolumeclaims.ts diff --git a/src/components/tables/pods/columns.ts b/src/components/tables/pods.ts similarity index 73% rename from src/components/tables/pods/columns.ts rename to src/components/tables/pods.ts index 503b557..d51a7a5 100644 --- a/src/components/tables/pods/columns.ts +++ b/src/components/tables/pods.ts @@ -1,5 +1,6 @@ import { V1Pod } from "@kubernetes/client-node"; import { ColumnDef } from "@tanstack/vue-table"; +import { RowAction } from "@/components/tables/types"; export const columns: ColumnDef[] = [ { @@ -36,3 +37,18 @@ export const columns: ColumnDef[] = [ accessorKey: "spec.nodeName", }, ]; + +export const rowActions: RowAction[] = [ + { + label: "Describe", + handler: (row) => { + console.log("Describe", row); + }, + }, + { + label: "Logs", + handler: (row) => { + console.log("Logs", row); + }, + }, +]; diff --git a/src/components/tables/secrets/columns.ts b/src/components/tables/secrets.ts similarity index 100% rename from src/components/tables/secrets/columns.ts rename to src/components/tables/secrets.ts diff --git a/src/components/tables/services/columns.ts b/src/components/tables/services.ts similarity index 100% rename from src/components/tables/services/columns.ts rename to src/components/tables/services.ts diff --git a/src/components/tables/types.ts b/src/components/tables/types.ts new file mode 100644 index 0000000..3424c5c --- /dev/null +++ b/src/components/tables/types.ts @@ -0,0 +1,4 @@ +export interface RowAction { + label: string; + handler: (row: T) => void; +} diff --git a/src/components/tables/virtualservices/columns.ts b/src/components/tables/virtualservices.ts similarity index 100% rename from src/components/tables/virtualservices/columns.ts rename to src/components/tables/virtualservices.ts diff --git a/src/components/ui/DataTable.vue b/src/components/ui/DataTable.vue index ca45b19..abfa703 100644 --- a/src/components/ui/DataTable.vue +++ b/src/components/ui/DataTable.vue @@ -1,6 +1,13 @@ + + diff --git a/src/components/ui/alert/AlertDescription.vue b/src/components/ui/alert/AlertDescription.vue new file mode 100644 index 0000000..a539b63 --- /dev/null +++ b/src/components/ui/alert/AlertDescription.vue @@ -0,0 +1,13 @@ + + + diff --git a/src/components/ui/alert/AlertTitle.vue b/src/components/ui/alert/AlertTitle.vue new file mode 100644 index 0000000..c5dd22c --- /dev/null +++ b/src/components/ui/alert/AlertTitle.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/components/ui/alert/index.ts b/src/components/ui/alert/index.ts new file mode 100644 index 0000000..499ee68 --- /dev/null +++ b/src/components/ui/alert/index.ts @@ -0,0 +1,21 @@ +import { cva } from 'class-variance-authority' + +export { default as Alert } from './Alert.vue' +export { default as AlertTitle } from './AlertTitle.vue' +export { default as AlertDescription } from './AlertDescription.vue' + +export const alertVariants = cva( + 'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7', + { + variants: { + variant: { + default: 'bg-background text-foreground', + destructive: + 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) diff --git a/src/components/ui/command/CommandDialog.vue b/src/components/ui/command/CommandDialog.vue index 345eb3e..9d16399 100644 --- a/src/components/ui/command/CommandDialog.vue +++ b/src/components/ui/command/CommandDialog.vue @@ -1,6 +1,6 @@ + + diff --git a/src/components/ui/context-menu/ContextMenuCheckboxItem.vue b/src/components/ui/context-menu/ContextMenuCheckboxItem.vue new file mode 100644 index 0000000..aca044c --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuCheckboxItem.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuContent.vue b/src/components/ui/context-menu/ContextMenuContent.vue new file mode 100644 index 0000000..4e201ff --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuContent.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuGroup.vue b/src/components/ui/context-menu/ContextMenuGroup.vue new file mode 100644 index 0000000..b7458d7 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuGroup.vue @@ -0,0 +1,11 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuItem.vue b/src/components/ui/context-menu/ContextMenuItem.vue new file mode 100644 index 0000000..0071251 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuItem.vue @@ -0,0 +1,27 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuLabel.vue b/src/components/ui/context-menu/ContextMenuLabel.vue new file mode 100644 index 0000000..3702f39 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuLabel.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuPortal.vue b/src/components/ui/context-menu/ContextMenuPortal.vue new file mode 100644 index 0000000..73dc714 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuPortal.vue @@ -0,0 +1,11 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuRadioGroup.vue b/src/components/ui/context-menu/ContextMenuRadioGroup.vue new file mode 100644 index 0000000..33273a7 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuRadioGroup.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuRadioItem.vue b/src/components/ui/context-menu/ContextMenuRadioItem.vue new file mode 100644 index 0000000..298e6f7 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuRadioItem.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuSeparator.vue b/src/components/ui/context-menu/ContextMenuSeparator.vue new file mode 100644 index 0000000..bf1eca8 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuSeparator.vue @@ -0,0 +1,13 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuShortcut.vue b/src/components/ui/context-menu/ContextMenuShortcut.vue new file mode 100644 index 0000000..e97f3c2 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuShortcut.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuSub.vue b/src/components/ui/context-menu/ContextMenuSub.vue new file mode 100644 index 0000000..7abc360 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuSub.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuSubContent.vue b/src/components/ui/context-menu/ContextMenuSubContent.vue new file mode 100644 index 0000000..bfdf27b --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuSubContent.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuSubTrigger.vue b/src/components/ui/context-menu/ContextMenuSubTrigger.vue new file mode 100644 index 0000000..6469f5f --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuSubTrigger.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/components/ui/context-menu/ContextMenuTrigger.vue b/src/components/ui/context-menu/ContextMenuTrigger.vue new file mode 100644 index 0000000..9747cc1 --- /dev/null +++ b/src/components/ui/context-menu/ContextMenuTrigger.vue @@ -0,0 +1,11 @@ + + + diff --git a/src/components/ui/context-menu/index.ts b/src/components/ui/context-menu/index.ts new file mode 100644 index 0000000..157f7b1 --- /dev/null +++ b/src/components/ui/context-menu/index.ts @@ -0,0 +1,14 @@ +export { default as ContextMenu } from './ContextMenu.vue' +export { default as ContextMenuTrigger } from './ContextMenuTrigger.vue' +export { default as ContextMenuContent } from './ContextMenuContent.vue' +export { default as ContextMenuGroup } from './ContextMenuGroup.vue' +export { default as ContextMenuRadioGroup } from './ContextMenuRadioGroup.vue' +export { default as ContextMenuItem } from './ContextMenuItem.vue' +export { default as ContextMenuCheckboxItem } from './ContextMenuCheckboxItem.vue' +export { default as ContextMenuRadioItem } from './ContextMenuRadioItem.vue' +export { default as ContextMenuShortcut } from './ContextMenuShortcut.vue' +export { default as ContextMenuSeparator } from './ContextMenuSeparator.vue' +export { default as ContextMenuLabel } from './ContextMenuLabel.vue' +export { default as ContextMenuSub } from './ContextMenuSub.vue' +export { default as ContextMenuSubTrigger } from './ContextMenuSubTrigger.vue' +export { default as ContextMenuSubContent } from './ContextMenuSubContent.vue' diff --git a/src/main.ts b/src/main.ts index 409462e..d779051 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,3 +10,11 @@ if (process.env.NODE_ENV === "development") { } createApp(App).use(router).mount("#app"); + +window.addEventListener("contextmenu", (e) => e.preventDefault()); + +window.addEventListener("keydown", (e) => { + if (e.metaKey && e.key === "r") { + window.location.reload(); + } +}); diff --git a/src/providers/CommandPaletteProvider.ts b/src/providers/CommandPaletteProvider.ts index e09aeb5..5721a17 100644 --- a/src/providers/CommandPaletteProvider.ts +++ b/src/providers/CommandPaletteProvider.ts @@ -24,6 +24,7 @@ export interface CommandPaletteState { commands: Command[]; commandCache: Map; loading: boolean; + executionError: string | null; } export default { @@ -36,6 +37,7 @@ export default { commands: [], commandCache: new Map(), loading: false, + executionError: null, }); provide(CommandPaletteStateKey, toRefs(state)); @@ -97,17 +99,26 @@ export default { push(command, state.commandCache.get(command.id) as Command[]); } - command.commands().then((commands: Command[]) => { - state.commandCache.set(command.id, commands); - - if (state.callStack.has(command)) { - state.callStack.delete(command); - } - - push(command, commands); - - state.loading = false; - }); + command + .commands() + .then((commands: Command[]) => { + state.commandCache.set(command.id, commands); + + if (state.callStack.has(command)) { + state.callStack.delete(command); + } + + push(command, commands); + + state.loading = false; + }) + .catch((error) => { + state.loading = false; + state.executionError = error.message; + setTimeout(() => { + state.executionError = null; + }, 2500); + }); } else { command.execute(); clearStack(); diff --git a/src/views/ConfigMaps.vue b/src/views/ConfigMaps.vue index b128924..9aaf661 100644 --- a/src/views/ConfigMaps.vue +++ b/src/views/ConfigMaps.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/configmaps/columns"; +import { columns } from "@/components/tables/configmaps"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -52,3 +52,4 @@ x +@/components/tables/configmaps/configmaps diff --git a/src/views/CronJobs.vue b/src/views/CronJobs.vue index 747dc7c..8a240e8 100644 --- a/src/views/CronJobs.vue +++ b/src/views/CronJobs.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/cronjobs/columns"; +import { columns } from "@/components/tables/cronjobs"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -52,3 +52,4 @@ x +@/components/tables/cronjobs/cronjobs diff --git a/src/views/Deployments.vue b/src/views/Deployments.vue index dcd9f62..8c642cc 100644 --- a/src/views/Deployments.vue +++ b/src/views/Deployments.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/deployments/columns"; +import { columns } from "@/components/tables/deployments"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -51,3 +51,4 @@ const { startRefreshing, stopRefreshing } = useDataRefresher( +@/components/tables/deployments/deployments diff --git a/src/views/Ingresses.vue b/src/views/Ingresses.vue index a3e2fe1..d31cd94 100644 --- a/src/views/Ingresses.vue +++ b/src/views/Ingresses.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/ingresses/columns"; +import { columns } from "@/components/tables/ingresses"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -51,3 +51,4 @@ const { startRefreshing, stopRefreshing } = useDataRefresher( +@/components/tables/ingresses/ingresses diff --git a/src/views/Jobs.vue b/src/views/Jobs.vue index 71b4925..c0cd422 100644 --- a/src/views/Jobs.vue +++ b/src/views/Jobs.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/jobs/columns"; +import { columns } from "@/components/tables/jobs"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -51,3 +51,4 @@ x +@/components/tables/jobs/jobs diff --git a/src/views/PersistentVolumeClaims.vue b/src/views/PersistentVolumeClaims.vue index 4d17004..17009b5 100644 --- a/src/views/PersistentVolumeClaims.vue +++ b/src/views/PersistentVolumeClaims.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/persistentvolumeclaims/columns"; +import { columns } from "@/components/tables/persistentvolumeclaims"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -52,3 +52,4 @@ x +@/components/tables/persistentvolumeclaims/persistentvolumeclaims diff --git a/src/views/Pods.vue b/src/views/Pods.vue index 547ad2f..b676495 100644 --- a/src/views/Pods.vue +++ b/src/views/Pods.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/pods/columns"; +import { columns, rowActions } from "@/components/tables/pods"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -49,5 +49,5 @@ const { startRefreshing, stopRefreshing } = useDataRefresher(getPods, 1000, [ x diff --git a/src/views/Secrets.vue b/src/views/Secrets.vue index a0e7f55..7726fc1 100644 --- a/src/views/Secrets.vue +++ b/src/views/Secrets.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/secrets/columns"; +import { columns } from "@/components/tables/secrets"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -52,3 +52,4 @@ x +@/components/tables/secrets/secrets diff --git a/src/views/Services.vue b/src/views/Services.vue index 8a72af5..a8a29c2 100644 --- a/src/views/Services.vue +++ b/src/views/Services.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/services/columns"; +import { columns } from "@/components/tables/services"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -51,3 +51,4 @@ const { startRefreshing, stopRefreshing } = useDataRefresher( +@/components/tables/services/services diff --git a/src/views/VirtualServices.vue b/src/views/VirtualServices.vue index f2e9f50..8fe11ca 100644 --- a/src/views/VirtualServices.vue +++ b/src/views/VirtualServices.vue @@ -9,7 +9,7 @@ import { KubeContextStateKey } from "@/providers/KubeContextProvider"; const { context, namespace } = injectStrict(KubeContextStateKey); import DataTable from "@/components/ui/DataTable.vue"; -import { columns } from "@/components/tables/virtualservices/columns"; +import { columns } from "@/components/tables/virtualservices"; import { useDataRefresher } from "@/composables/refresher"; const { toast } = useToast(); @@ -51,3 +51,4 @@ const { startRefreshing, stopRefreshing } = useDataRefresher( +@/components/tables/virtualservices/virtualservices diff --git a/tailwind.config.js b/tailwind.config.js index 2a58b8a..6fd7346 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -2,8 +2,6 @@ const animate = require("tailwindcss-animate"); /** @type {import('tailwindcss').Config} */ module.exports = { - important: true, - darkMode: ["class"], content: [