This commit is contained in:
marzban-dev
2025-04-12 01:05:27 +03:30
21 changed files with 145 additions and 45 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
from django.contrib import admin
from .models import *
from unfold.admin import ModelAdmin, TabularInline
from unfold.admin import TabularInline
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from import_export.admin import ImportExportModelAdmin
from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm
+2 -1
View File
@@ -11,6 +11,7 @@ urlpatterns = [
path('cart/all', CartItemClear.as_view()),
path('cart/item/<int:pk>', CartItemViews.as_view(), name='change-item-cart'),
path('cart/payment', PaymentView.as_view(), name='payment'),
path('callback', callback_view, name='callback-gateway'),
# path('callback', callback_view, name='callback-gateway'),
path('transaction/<int:tracking_code>', CallbackView.as_view(), name='callback-gateway'),
path('<int:pk>', OrderGetView.as_view(), name='order-get'),
]
+19 -2
View File
@@ -222,8 +222,7 @@ class PaymentView(APIView):
)
bank.set_request(request)
bank.set_amount(amount)
bank.set_client_callback_url(reverse("callback-gateway"))
print(reverse('callback-gateway'))
bank.set_client_callback_url('http://localhost:3000/transaction')
bank.set_mobile_number(user_mobile_number)
bank_record = bank.ready()
@@ -270,6 +269,24 @@ def callback_view(request):
class CallbackView(APIView):
def get(self, request, tracking_code):
if not tracking_code:
return Response({'detail': 'کد تریسکد درست نمیباشد.'})
try:
bank_record = bank_models.Bank.objects.get(tracking_code=tracking_code)
except bank_models.Bank.DoesNotExist:
return Response({'detail': 'کد تریسکد معتبر نمیباشد.'}, status=status.HTTP_400_BAD_REQUEST)
if bank_record.is_success:
return Response({"detail" : "پرداخت با موفقیت انجام شد."}, status=status.HTTP_200_OK)
return Response(
{"detail": "پرداخت با شکست مواجه شده است. اگر پول کم شده است ظرف مدت ۴۸ ساعت پول به حساب شما بازخواهد گشت."}, status=status.HTTP_404_NOT_FOUND
)
class SetAddressSerilizer(serializers.Serializer):
address_id = serializers.IntegerField()
@@ -24,7 +24,7 @@ const emit = defineEmits<Emits>();
@click="emit('select', null)"
:class="
isSelected
? 'ring-2 ring-offset-2 ring-black border-black'
? 'ring-2 ring-offset-2 ring-blue-500 border-blue-500'
: 'border-slate-200'
"
class="w-full p-5 border rounded-xl flex flex-col gap-4 transition-all cursor-pointer relative overflow-hidden"
@@ -41,7 +41,7 @@ const emit = defineEmits<Emits>();
>
<span
v-if="isSelected"
class="bg-black rounded-md p-0.5 text-center bottom-4 left-4 text-slate-200 text-[10px] lg:text-xs absolute"
class="bg-blue-500 rounded-md p-0.5 text-center bottom-4 left-4 text-slate-200 text-[10px] lg:text-xs absolute"
>
<Icon name="bi:check" size="20" class="**:fill-white" />
</span>
@@ -43,21 +43,14 @@ const visible = computed({
class="bg-custom-conic size-[200%] absolute -top-1/2 -left-1/2 animate-spin [animation-duration:3s] z-[1]"
></div>
<div
class="shrink-0 p-6 pt-16 z-[2] absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 w-[98.8%] h-[98.5%] flex flex-col gap-10 items-center bg-white border shadow-black/10 justify-center border-slate-300 rounded-xl"
class="shrink-0 p-6 pt-20 z-[2] absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 w-[98.8%] h-[98.5%] flex flex-col gap-24 items-center bg-white border shadow-black/10 justify-center border-slate-300 rounded-xl"
>
<video
class="aspect-square w-[350px] animate-fade-in"
src="/video/heymlz/heymlz-handshake-full.webm"
:style="{
filter: 'drop-shadow(0px 4px 20px rgba(0, 0, 0, 0.15))',
}"
autoplay
playsinline
webkit-playsinline
muted
<NuxtImg
class="aspect-square w-[300px]"
src="/img/heymlz/payment-progress.gif"
/>
<div class="-translate-y-24 flex-center gap-3">
<div class="-translate-y-28 flex-center gap-3">
<Icon
name="svg-spinners:3-dots-bounce"
size="20"
@@ -84,6 +77,10 @@ const visible = computed({
#ff000000 1%,
#ff000000 99%
),
conic-gradient(from 0deg at 50% 50%, #000000ff 0%, #ff000000 34%);
conic-gradient(
from 0deg at 50% 50%,
var(--color-blue-500) 0%,
#ff000000 34%
);
}
</style>
@@ -66,7 +66,7 @@ const handleDeleteAddress = (id: number) => {
@click.prevent="!!address ? emit('select', address) : null"
:class="
isSelected
? 'border-slate-200 ring-2 ring-offset-2 ring-black'
? 'border-transparent ring-2 ring-offset-2 ring-blue-500'
: 'border-slate-200'
"
class="flex flex-col items-center transition-all relative cursor-pointer w-full group gap-2 lg:gap-4 p-4 border rounded-xl bg-slate-50 overflow-hidden"
@@ -81,7 +81,7 @@ const handleDeleteAddress = (id: number) => {
{{ !!address ? address.name : "آدرس" }}
<span
v-if="isSelected"
class="bg-black rounded-lg px-3 py-2 text-slate-200 text-[10px] lg:text-xs"
class="bg-blue-500 rounded-lg px-3 py-2 text-slate-200 text-[10px] lg:text-xs"
>
انتخاب شده
</span>
@@ -135,6 +135,8 @@ watch(
:end-icon="!!address ? 'bi:pen' : 'ci:plus'"
size="md"
class="rounded-full"
:variant="!!address ? 'ghost' : 'solid'"
:class="!!address ? '!bg-transparent !underline' : ''"
>
<span class="whitespace-pre">
{{ !!address ? "ویرایش" : "افزودن آدرس" }}
@@ -181,7 +181,7 @@ const handlePayment = () => {
? handleDeleteDiscountCode()
: handleSubmitDiscountCode()
"
class="text-xs px-5 rounded-e-100 py-1.5 text-white bg-black hover:invert border-[1.5px] border-black hover:border-white transition-all disabled:cursor-not-allowed"
class="text-xs px-5 rounded-e-100 py-1.5 text-white bg-blue-500 hover:bg-transparent hover:text-blue-500 border-[1.5px] border-blue-500 hover:border-white transition-all disabled:cursor-not-allowed"
:disabled="
!discountCode.length ||
submitDiscountCodeIsPending ||
@@ -206,8 +206,9 @@ const handlePayment = () => {
<Button
v-if="nextPage?.name == 'payment'"
start-icon="ci:arrow-right"
class="w-full rounded-100 bg-blue-500"
class="w-full rounded-100"
@click="handlePayment"
variant="primary"
>
{{ nextPage?.label }}
</Button>
@@ -216,7 +217,11 @@ const handlePayment = () => {
v-else
:to="{ name: nextPage?.name, query: { gw: nextPage?.query } }"
>
<Button start-icon="arrow-right" class="w-full rounded-100">
<Button
start-icon="arrow-right"
class="w-full rounded-100"
variant="primary"
>
{{ nextPage?.label }}
</Button>
</NuxtLink>
+1
View File
@@ -58,6 +58,7 @@ export const API_ENDPOINTS = {
},
checkout: {
payment: "/order/cart/payment",
transaction: "/order/transaction",
},
},
};
+5 -3
View File
@@ -8,7 +8,9 @@ const route = useRoute();
// computed
const pageTitle = computed(() => route.meta.pageTitle);
const prevPage = computed(() => route.meta.prevPage as { name: string, label: string } | undefined);
const prevPage = computed(
() => route.meta.prevPage as { name: string; label: string } | undefined
);
// queries
@@ -47,9 +49,9 @@ const hasCartItem = computed(
>
<Icon
name="bi:arrow-right"
class="**:stroke-cyan-400"
class="**:stroke-blue-500"
/>
<span class="text-cyan-400">
<span class="text-blue-500">
{{ prevPage?.label }}
</span>
</NuxtLink>
+1
View File
@@ -8,6 +8,7 @@ definePageMeta({
middleware: "check-is-logged-in",
pageTitle: "ثبت سفارش",
prevPage: { name: "cart-delivery", label: "انتخاب آدرس" },
nextPage: { name: "payment", label: "پرداخت" },
});
// state
+9 -7
View File
@@ -94,12 +94,14 @@ whenever(
<div class="flex flex-col w-full gap-5">
<AddressItem />
<div class="flex flex-col w-full gap-6">
<div class="flex items-center gap-3">
<span class="typo-sub-h-xl"> آدرس های شما </span>
<div class="flex items-center gap-3 py-3">
<NuxtImg src="/img/location.gif" class="size-12 pb-1 -mr-3" />
<span class="typo-sub-h-xl -mr-3"> آدرس های شما </span>
<Icon
name="svg-spinners:180-ring-with-bg"
size="20"
v-if="setOrderAddressIsPending"
v-if="!setOrderAddressIsPending"
class="pb-0.5"
/>
</div>
<div v-if="addressesIsLoading" class="flex flex-col gap-6 w-full">
@@ -146,14 +148,14 @@ whenever(
? 'ring-black ring-offset-2 ring-2'
: ''
"
class="flex flex-col select-none w-full gap-2 p-3 transition-all border cursor-pointer delivery-option focus-within:ring-2 ring-black ring-offset-2 focus-within:border-black rounded-100 border-slate-200 bg-slate-50"
class="flex flex-col select-none w-full gap-2 p-3 transition-all border cursor-pointer delivery-option focus-within:ring-2 ring-blue-500 ring-offset-2 focus-within:border-blue-500 rounded-100 border-slate-200 bg-slate-50"
>
<div class="flex items-center justify-between w-full">
<div class="flex items-center gap-2.5">
<SwitchRoot
v-model="deliveryData.deliveryMethod.pishtaz"
:defaultValue="false"
class="w-[3rem] h-[1.8rem] shrink-0 flex data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-black border border-slate-200 data-[state=checked]:border-black/20 rounded-full relative transition-all focus-within:outline-none"
class="w-[3rem] h-[1.8rem] shrink-0 flex data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-blue-500 border border-slate-200 data-[state=checked]:border-blue-500/20 rounded-full relative transition-all focus-within:outline-none"
>
<SwitchThumb
class="size-6 my-auto bg-white text-sm ms-1 flex items-center justify-center shadow-xl rounded-full transition-transform translate-x-0.5 will-change-transform data-[state=checked]:-translate-x-[68%]"
@@ -172,12 +174,12 @@ whenever(
</label>
<label
class="flex items-center opacity-50 select-none pointer-events-none justify-between w-full p-3 transition-all border cursor-pointer delivery-option focus-within:ring-2 ring-black ring-offset-2 focus-within:border-black rounded-100 border-slate-200 bg-slate-50"
class="flex items-center opacity-50 select-none pointer-events-none justify-between w-full p-3 transition-all border cursor-pointer delivery-option focus-within:ring-2 ring-blue-500 ring-offset-2 focus-within:border-blue-500 rounded-100 border-slate-200 bg-slate-50"
>
<div class="flex items-center gap-2.5">
<SwitchRoot
v-model="deliveryData.deliveryMethod.tipax"
class="w-[3rem] h-[1.8rem] shrink-0 flex data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-black border border-slate-200 data-[state=checked]:border-black/20 rounded-full relative transition-all focus-within:outline-none"
class="w-[3rem] h-[1.8rem] shrink-0 flex data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-blue-500 border border-slate-200 data-[state=checked]:border-blue-500/20 rounded-full relative transition-all focus-within:outline-none"
>
<SwitchThumb
class="size-6 my-auto bg-white text-sm ms-1 flex items-center justify-center shadow-xl rounded-full transition-transform translate-x-0.5 will-change-transform data-[state=checked]:-translate-x-[68%]"
+2 -1
View File
@@ -35,7 +35,8 @@ const hasCartItem = computed(
class="!w-36 !h-[43px] !rounded-lg"
/>
<div v-else class="flex items-center w-full gap-3 lg:w-1/2">
<div v-else class="flex items-center w-full gap-2 lg:w-1/2">
<NuxtImg src="/img/box.gif" class="size-12 pb-2" />
<p class="font-semibold lg:text-lg text-black">
{{ cart?.items.length }} مرسوله
</p>
-1
View File
@@ -41,7 +41,6 @@ onMounted(() => {
/>
<Categories class="mt-40" />
<Brands />
<!-- <MostRecentComments />-->
<ClientOnly>
<LatestStories class="mb-20" />
</ClientOnly>
+5 -2
View File
@@ -82,7 +82,7 @@ watch(
placeholder="جست و جو محصول ..."
v-model="search"
variant="outlined"
class="rounded-full w-full lg:w-8/12"
class="!rounded-full w-full lg:w-8/12"
>
<template #endItem>
<div class="flex items-center gap-1">
@@ -131,7 +131,10 @@ watch(
:products="products!"
class="!p-0"
/>
<div v-if="data && paginationData && data.count > 10" class="w-full flex-center py-10">
<div
v-if="data && paginationData && data.count > 10"
class="w-full flex-center py-10"
>
<Pagination :items="paginationData" :total="data.count" />
</div>
</div>
+76
View File
@@ -0,0 +1,76 @@
<script setup lang="ts">
// meta
definePageMeta({
layout: "none",
});
</script>
<template>
<div class="w-full flex-col-center gap-3 h-svh relative">
<div
class="bg-[url(/img/pattern-1.png)] -z-10 size-full fixed inset-0 opacity-70"
:style="{
backgroundSize: 150,
mask: 'linear-gradient(to bottom, black 0%, rgba(0,0,0,0.3) 80%)',
}"
/>
<NuxtImg src="/logo/logo-col.png" class="size-44 -mt-12" />
<div
class="max-w-[500px] w-full p-6 gap-6 flex flex-col items-center bg-white border shadow-black/10 justify-center border-slate-300 rounded-3xl relative overflow-hidden"
>
<div
class="w-full h-[5rem] bg-success-500 absolute left-0 top-0 flex-center gap-2 text-white"
>
<Icon name="bi:check" size="28" />
<h1 class="typo-h-6 font-normal">تراکنش موفق</h1>
</div>
<div class="w-full flex flex-col gap-5 pt-[5.5rem] p-1">
<div
class="w-full flex flex-row-reverse items-center justify-between"
>
<span>مبلغ تراكنش </span>
<span>١٢٣ تومان</span>
</div>
<div
class="w-full flex flex-row-reverse items-center justify-between"
>
<span>شماره پيكَيرى </span>
<span>١٢٣ تومان</span>
</div>
<div
class="w-full flex flex-row-reverse items-center justify-between"
>
<span>شماره ارجاع </span>
<span>١٢٣ تومان</span>
</div>
<div
class="w-full flex flex-row-reverse items-center justify-between"
>
<span>تاريخ و ساعت </span>
<span>١٢٣ تومان</span>
</div>
</div>
<div class="w-full flex items-center justify-between gap-5">
<NuxtLink to="/" class="w-full">
<Button
class="w-full rounded-full"
start-icon="ci:left-rotation"
variant="secondary"
>بازگشت به فروشگاه</Button
>
</NuxtLink>
<Button
class="w-full rounded-full bg-success-500 hover:text-success-500 hover:border-success-500 hover:**:!stroke-success-500"
start-icon="ci:share"
variant="primary"
>دانلود فاکتور</Button
>
</div>
</div>
</div>
</template>
-7
View File
@@ -1,7 +0,0 @@
<script setup lang="ts"></script>
<template>
<div class="w-full h-svh flex"></div>
</template>
<style scoped></style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB