Complete super admin panel with CRUD, bookings, profile, and dev API proxy.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-21 14:20:52 +03:30
co-authored by Cursor
parent 03f3994121
commit 4bc6c003f3
31 changed files with 1508 additions and 43 deletions
+30
View File
@@ -0,0 +1,30 @@
<template>
<span
class="inline-flex rounded-lg px-2 py-0.5 text-xs font-medium"
:class="toneClass"
>
<slot />
</span>
</template>
<script setup lang="ts">
import { computed } from 'vue';
const props = withDefaults(
defineProps<{ tone?: 'default' | 'success' | 'warning' | 'danger' }>(),
{ tone: 'default' },
);
const toneClass = computed(() => {
switch (props.tone) {
case 'success':
return 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400';
case 'warning':
return 'bg-amber-500/15 text-amber-700 dark:text-amber-400';
case 'danger':
return 'bg-red-500/15 text-red-600 dark:text-red-400';
default:
return 'bg-surface-3 text-muted';
}
});
</script>
+3
View File
@@ -5,6 +5,7 @@
</label>
<input
:id="id"
v-model="model"
class="w-full rounded-xl border border-border bg-surface-2 px-4 py-3 text-text outline-none transition placeholder:text-muted focus:border-brand-500 focus:ring-2 focus:ring-brand-500/30"
:class="ltr ? 'text-left' : ''"
v-bind="$attrs"
@@ -15,6 +16,8 @@
<script setup lang="ts">
defineOptions({ inheritAttrs: false });
const model = defineModel<string>({ default: '' });
withDefaults(
defineProps<{
label?: string;
+40
View File
@@ -0,0 +1,40 @@
<template>
<Teleport to="body">
<div
v-if="open"
class="fixed inset-0 z-50 flex items-end justify-center bg-black/50 p-0 sm:items-center sm:p-4"
@click.self="emit('close')"
>
<div
class="max-h-[90dvh] w-full max-w-app overflow-y-auto rounded-t-2xl border border-border bg-surface p-4 shadow-xl sm:rounded-2xl"
role="dialog"
>
<div class="mb-4 flex items-start justify-between gap-3">
<div>
<h2 v-if="title" class="text-lg font-bold text-text">{{ title }}</h2>
<p v-if="subtitle" class="mt-0.5 text-sm text-muted">{{ subtitle }}</p>
</div>
<button
type="button"
class="rounded-lg px-2 py-1 text-muted hover:bg-surface-3"
aria-label="بستن"
@click="emit('close')"
>
✕
</button>
</div>
<slot />
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
defineProps<{
open: boolean;
title?: string;
subtitle?: string;
}>();
const emit = defineEmits<{ close: [] }>();
</script>
+36
View File
@@ -0,0 +1,36 @@
<template>
<div class="space-y-1.5">
<label v-if="label" :for="id" class="block text-sm font-medium text-muted">
{{ label }}
</label>
<select
:id="id"
:value="modelValue"
class="w-full rounded-xl border border-border bg-surface-2 px-4 py-3 text-text outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/30"
:class="ltr ? 'text-left' : ''"
@change="onChange"
>
<option v-if="placeholder" disabled value="">{{ placeholder }}</option>
<option v-for="opt in options" :key="opt.value" :value="opt.value">
{{ opt.label }}
</option>
</select>
</div>
</template>
<script setup lang="ts">
defineProps<{
label?: string;
id?: string;
modelValue: string;
options: { value: string; label: string }[];
placeholder?: string;
ltr?: boolean;
}>();
const emit = defineEmits<{ 'update:modelValue': [value: string] }>();
function onChange(e: Event) {
emit('update:modelValue', (e.target as HTMLSelectElement).value);
}
</script>
+30
View File
@@ -0,0 +1,30 @@
<template>
<div class="space-y-1.5">
<label v-if="label" :for="id" class="block text-sm font-medium text-muted">
{{ label }}
</label>
<textarea
:id="id"
v-model="model"
:rows="rows"
class="w-full resize-y rounded-xl border border-border bg-surface-2 px-4 py-3 text-text outline-none transition placeholder:text-muted focus:border-brand-500 focus:ring-2 focus:ring-brand-500/30"
v-bind="$attrs"
/>
</div>
</template>
<script setup lang="ts">
defineOptions({ inheritAttrs: false });
const model = defineModel<string>({ default: '' });
withDefaults(
defineProps<{
label?: string;
id?: string;
rows?: number;
}>(),
{ rows: 3 },
);
</script>
+7
View File
@@ -0,0 +1,7 @@
<template>
<div
class="rounded-2xl border border-dashed border-border bg-surface-2 px-4 py-8 text-center text-sm text-muted"
>
<slot />
</div>
</template>
+13
View File
@@ -0,0 +1,13 @@
<template>
<div class="flex items-start justify-between gap-3">
<div>
<h1 class="text-xl font-bold text-text">{{ title }}</h1>
<p v-if="subtitle" class="mt-1 text-sm text-muted">{{ subtitle }}</p>
</div>
<slot name="actions" />
</div>
</template>
<script setup lang="ts">
defineProps<{ title: string; subtitle?: string }>();
</script>