2026-09-23 01:42:07 +03:30
|
|
|
<!--
|
|
|
|
|
"تکمیل پروفایل" — the customer uploads their three documents.
|
|
|
|
|
|
|
|
|
|
Ported from henza_pwa/src/views/user/src/completeProfile.vue (route
|
|
|
|
|
/complete_profile, name completeProfile), restyled into this app's design
|
|
|
|
|
language. The LOGIC is the reference's and is identical in user-app; only
|
|
|
|
|
the styling differs.
|
|
|
|
|
|
|
|
|
|
Kept from the reference:
|
|
|
|
|
- the header title "تکمیل پروفایل" (completeProfile.vue:12) and the back
|
|
|
|
|
arrow returning to the dashboard (:11)
|
|
|
|
|
- exactly three uploads, in the reference's order and with its texts:
|
|
|
|
|
جواز کسب, سردر مغازه, کارت ملی (:45-56)
|
|
|
|
|
- the submit button "بارگذاری اطلاعات" (:81)
|
|
|
|
|
- the client-side all-three check BEFORE posting, with the reference's own
|
|
|
|
|
message "لطفا تمامی عکس های خواسته شده را آپلود کنید" (:191-198)
|
|
|
|
|
- POST with the three snake_case ids (:193)
|
|
|
|
|
- the success dialog with its text and its "متوجه شدم" button (:94-111),
|
|
|
|
|
after which the reference navigated home (:175-181)
|
|
|
|
|
|
|
|
|
|
Deliberately NOT ported: the reference's `window.setInterval(... reload)` in
|
|
|
|
|
refresh() (:178-180), which reloaded the whole page every second FOREVER —
|
|
|
|
|
an unterminated timer, not a refresh. The store is re-read once instead.
|
|
|
|
|
|
|
|
|
|
The page is reachable only while the profile is incomplete; see the router
|
|
|
|
|
guard, which mirrors the reference's (henza_pwa router/index.ts:266).
|
|
|
|
|
-->
|
|
|
|
|
<template>
|
|
|
|
|
<div class="cpf">
|
|
|
|
|
<PageBar title="تکمیل پروفایل" back-to="userPage::hasSubRoute" />
|
|
|
|
|
|
|
|
|
|
<Spinner :spinning="submitting" tip="در حال ثبت اطلاعات">
|
|
|
|
|
<container class="cpf__body">
|
|
|
|
|
<p class="cpf__lead">
|
|
|
|
|
برای تکمیل پروفایل، سه مدرک زیر را بارگذاری کنید.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<form class="cpf__form" @submit.prevent="submit">
|
|
|
|
|
<SingleImageUploader
|
|
|
|
|
upload-text="بارگذاری عکس جواز کسب"
|
|
|
|
|
purpose="business-license"
|
|
|
|
|
@done="(id: string) => (form.business_license_image_id = id)"
|
|
|
|
|
/>
|
|
|
|
|
<SingleImageUploader
|
|
|
|
|
upload-text="بارگذاری عکس سردر مغازه"
|
|
|
|
|
purpose="shop-image"
|
|
|
|
|
@done="(id: string) => (form.shop_image_id = id)"
|
|
|
|
|
/>
|
|
|
|
|
<SingleImageUploader
|
|
|
|
|
upload-text="بارگذاری عکس کارت ملی"
|
|
|
|
|
purpose="national-card"
|
|
|
|
|
@done="(id: string) => (form.ncode_image_id = id)"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<p v-if="error" class="cpf__error">{{ error }}</p>
|
|
|
|
|
|
|
|
|
|
<button type="submit" class="cpf__submit" :disabled="submitting">
|
|
|
|
|
بارگذاری اطلاعات
|
|
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
</container>
|
|
|
|
|
</Spinner>
|
|
|
|
|
|
2026-10-04 13:57:25 +03:30
|
|
|
<IslandSheet
|
2026-09-23 01:42:07 +03:30
|
|
|
v-model:show="done"
|
2026-10-04 13:57:25 +03:30
|
|
|
art="success"
|
|
|
|
|
title="تکمیل اطلاعات شما با موفقیت انجام شد."
|
|
|
|
|
description="مدارک شما با موفقیت بارگذاری شد."
|
|
|
|
|
confirm-text="متوجه شدم"
|
2026-09-23 01:42:07 +03:30
|
|
|
:close-on-click-overlay="false"
|
2026-10-04 13:57:25 +03:30
|
|
|
@confirm="finish"
|
|
|
|
|
/>
|
2026-09-23 01:42:07 +03:30
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { reactive, ref } from 'vue'
|
|
|
|
|
import { useRouter } from 'vue-router'
|
2026-10-04 13:57:25 +03:30
|
|
|
import { toast } from '../../composables/useToast'
|
2026-09-23 01:42:07 +03:30
|
|
|
import PageBar from '../../components/ui/PageBar.vue'
|
|
|
|
|
import Spinner from '../../components/Spinner.vue'
|
2026-10-04 13:57:25 +03:30
|
|
|
import IslandSheet from '../../components/ui/IslandSheet.vue'
|
2026-09-23 01:42:07 +03:30
|
|
|
import SingleImageUploader from '../../components/upload/SingleImageUploader.vue'
|
|
|
|
|
import { api } from '../../api/client'
|
2026-10-11 14:31:32 +03:30
|
|
|
import { KEYS, useQueryCache } from '../../stores/queryCache'
|
2026-09-23 01:42:07 +03:30
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
|
|
|
|
const form = reactive({
|
|
|
|
|
business_license_image_id: null as string | null,
|
|
|
|
|
shop_image_id: null as string | null,
|
|
|
|
|
ncode_image_id: null as string | null,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const submitting = ref(false)
|
|
|
|
|
const done = ref(false)
|
|
|
|
|
const error = ref<string | null>(null)
|
|
|
|
|
|
|
|
|
|
async function submit() {
|
|
|
|
|
error.value = null
|
|
|
|
|
|
|
|
|
|
// The reference checked all three client-side before posting
|
|
|
|
|
// (completeProfile.vue:191-198); the server requires them too.
|
|
|
|
|
if (!form.business_license_image_id || !form.shop_image_id || !form.ncode_image_id) {
|
2026-10-04 13:57:25 +03:30
|
|
|
toast.warning('لطفا تمامی عکس های خواسته شده را آپلود کنید')
|
2026-09-23 01:42:07 +03:30
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
submitting.value = true
|
|
|
|
|
try {
|
|
|
|
|
await api.completeProfile({
|
|
|
|
|
business_license_image_id: form.business_license_image_id,
|
|
|
|
|
shop_image_id: form.shop_image_id,
|
|
|
|
|
ncode_image_id: form.ncode_image_id,
|
|
|
|
|
})
|
2026-10-11 14:31:32 +03:30
|
|
|
useQueryCache().invalidate(KEYS.profile)
|
2026-09-23 01:42:07 +03:30
|
|
|
done.value = true
|
|
|
|
|
} catch {
|
|
|
|
|
error.value = 'ثبت اطلاعات انجام نشد. دوباره تلاش کنید.'
|
|
|
|
|
} finally {
|
|
|
|
|
submitting.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** The reference sent the user home after the dialog (:175-177). */
|
|
|
|
|
function finish() {
|
|
|
|
|
done.value = false
|
|
|
|
|
router.push({ name: 'home' })
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="less">
|
|
|
|
|
/*
|
|
|
|
|
v2 styling. The template and script above are IDENTICAL to user-app's — only
|
|
|
|
|
this block differs. v2 keeps the flat, hairline-separated rhythm of its other
|
|
|
|
|
screens, so the three slots run together as a continuous list rather than as
|
|
|
|
|
three spaced cards, and the action bar pins to the bottom.
|
|
|
|
|
*/
|
|
|
|
|
.cpf__body {
|
|
|
|
|
padding-top: 84px;
|
|
|
|
|
padding-bottom: 110px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cpf__lead {
|
|
|
|
|
margin: 4px 0 6px;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
color: @inkMuted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cpf__form {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cpf__error {
|
|
|
|
|
margin: 12px 2px 0;
|
|
|
|
|
font-size: 12.5px;
|
2026-10-04 16:18:56 +03:30
|
|
|
color: @red;
|
2026-09-23 01:42:07 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cpf__submit {
|
|
|
|
|
position: fixed;
|
|
|
|
|
left: 16px;
|
|
|
|
|
right: 16px;
|
|
|
|
|
bottom: 20px;
|
|
|
|
|
z-index: 20;
|
|
|
|
|
max-width: 736px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
background: @wine;
|
|
|
|
|
color: #fff;
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cpf__submit:disabled {
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
cursor: progress;
|
|
|
|
|
}
|
|
|
|
|
</style>
|