Complete super admin panel with CRUD, bookings, profile, and dev API proxy.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user