Add password auth, business browsing, dev proxy, and production env.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-23 13:14:52 +03:30
co-authored by Cursor
parent 3df62f9c6e
commit e07527c1db
23 changed files with 736 additions and 37 deletions
+27
View File
@@ -0,0 +1,27 @@
<template>
<div class="mb-4 flex rounded-xl border border-border bg-surface-3 p-1">
<button
type="button"
class="flex-1 rounded-lg py-2 text-sm font-medium transition"
:class="modelValue === 'otp' ? 'bg-surface text-text shadow-sm' : 'text-muted'"
@click="emit('update:modelValue', 'otp')"
>
کد یکبارمصرف
</button>
<button
type="button"
class="flex-1 rounded-lg py-2 text-sm font-medium transition"
:class="modelValue === 'password' ? 'bg-surface text-text shadow-sm' : 'text-muted'"
@click="emit('update:modelValue', 'password')"
>
رمز عبور
</button>
</div>
</template>
<script setup lang="ts">
export type AuthLoginMode = 'otp' | 'password';
defineProps<{ modelValue: AuthLoginMode }>();
const emit = defineEmits<{ 'update:modelValue': [value: AuthLoginMode] }>();
</script>
+46
View File
@@ -0,0 +1,46 @@
<template>
<RouterLink
:to="{ name: 'app-business-detail', params: { id: business.id } }"
class="block overflow-hidden rounded-2xl border border-border bg-surface-2 transition hover:border-brand-500/40 hover:shadow-md"
>
<div class="relative aspect-[16/10] bg-surface-3">
<img
v-if="cover"
:src="cover"
:alt="business.title"
class="h-full w-full object-cover"
loading="lazy"
/>
<div v-else class="flex h-full items-center justify-center text-3xl text-muted/40">🏪</div>
</div>
<div class="p-4">
<div class="flex items-start justify-between gap-2">
<h2 class="font-semibold text-text">{{ business.title }}</h2>
<span
v-if="business.category"
class="shrink-0 rounded-lg bg-brand-500/10 px-2 py-0.5 text-xs text-brand-700 dark:text-brand-500"
>
{{ business.category.title }}
</span>
</div>
<p v-if="business.description" class="mt-2 line-clamp-2 text-sm text-muted">
{{ business.description }}
</p>
<p v-if="business.address" class="mt-2 text-xs text-muted">
📍 {{ business.address }}
</p>
</div>
</RouterLink>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import type { Business } from '@/api/types';
import { businessCoverUrl } from '@/utils/media';
const props = defineProps<{
business: Business;
}>();
const cover = computed(() => businessCoverUrl(props.business.images));
</script>
@@ -0,0 +1,43 @@
<template>
<div class="-mx-1 flex gap-2 overflow-x-auto px-1 pb-1">
<button
type="button"
class="shrink-0 rounded-full px-4 py-2 text-sm font-medium transition"
:class="
!selectedId
? 'bg-brand-600 text-white'
: 'bg-surface-3 text-muted hover:text-text'
"
@click="$emit('select', null)"
>
همه
</button>
<button
v-for="cat in categories"
:key="cat.id"
type="button"
class="shrink-0 rounded-full px-4 py-2 text-sm font-medium transition"
:class="
selectedId === cat.id
? 'bg-brand-600 text-white'
: 'bg-surface-3 text-muted hover:text-text'
"
@click="$emit('select', cat.id)"
>
{{ cat.title }}
</button>
</div>
</template>
<script setup lang="ts">
import type { BusinessCategory } from '@/api/types';
defineProps<{
categories: BusinessCategory[];
selectedId: string | null;
}>();
defineEmits<{
select: [categoryId: string | null];
}>();
</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;