Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
name: Deploy to Server
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Copy files to server
|
||||
uses: appleboy/scp-action@v0.1.6
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
password: ${{ secrets.SSH_PASSWORD }}
|
||||
source: "."
|
||||
target: "/root/hshop/"
|
||||
|
||||
- name: SSH command to build and start Docker
|
||||
uses: appleboy/ssh-action@v0.1.6
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
password: ${{ secrets.SSH_PASSWORD }}
|
||||
script: |
|
||||
cd /root/hshop/
|
||||
docker compose down
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
+7
-7
@@ -2,11 +2,11 @@
|
||||
|
||||
DEBUG = True # keep debug true to set the database to sqlite
|
||||
# postgres database info
|
||||
DB_NAME = ''
|
||||
DB_USER = ''
|
||||
DB_PASSWORD = ''
|
||||
DB_HOST = ''
|
||||
DB_PORT = ''
|
||||
DB_NAME = 'hshop'
|
||||
DB_USER = 'byeto'
|
||||
DB_PASSWORD = 'vuhbyq-cypMu0-sirbon'
|
||||
DB_HOST = 'db'
|
||||
DB_PORT = 5434
|
||||
SECRET_KEY = '2h&gmi54wqauwqht48l-9c)r6_67_(oe_$ll%(+xz%u#)+of@d'
|
||||
# email stuff
|
||||
EMAIL_BACKEND = ''
|
||||
@@ -17,9 +17,9 @@ DEFAULT_FROM_EMAIL = ''
|
||||
# telegram bot toekn
|
||||
TELEGRAM_BOT_TOKEN = ''
|
||||
# domain for allowed host and allowed cors
|
||||
DOMAIN = ''
|
||||
DOMAIN = '38.60.202.91'
|
||||
# domain for api (the domain that django will use)
|
||||
API_DOMAIN = ''
|
||||
API_DOMAIN = '38.60.202.91'
|
||||
SITE_TITLE = ''
|
||||
SITE_HEADER = ''
|
||||
# jwt token configs
|
||||
|
||||
@@ -9,6 +9,7 @@ from drf_spectacular.utils import extend_schema, OpenApiParameter
|
||||
from rest_framework_simplejwt.views import TokenObtainPairView
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
import ghasedak_sms
|
||||
class SendOTPView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
@extend_schema(
|
||||
@@ -24,18 +25,45 @@ class SendOTPView(APIView):
|
||||
)
|
||||
def post(self, request):
|
||||
phone = request.data.get('phone')
|
||||
if not phone:
|
||||
return Response({'detail': 'Phone number is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
user, created = User.objects.get_or_create(phone=phone)
|
||||
print(created)
|
||||
print(user.phone)
|
||||
otp = user.set_otp()
|
||||
message = f"کد یک بار مصرف : {otp}"
|
||||
print(message)
|
||||
# send otp
|
||||
|
||||
|
||||
sms_api = ghasedak_sms.Ghasedak(api_key="4dc844abd4409fe247ec73831aed2498ad3749c1945660cc252654371756b966vafe5d9LGgMbnfGn")
|
||||
|
||||
# response = sms_api.send_single_sms(ghasedak_sms.SendSingleSmsInput(message=message, receptor=phone, line_number='30005006006908', send_date='', client_reference_id=''))
|
||||
# print(response)
|
||||
|
||||
|
||||
|
||||
response = sms_api.send_single_sms(
|
||||
ghasedak_sms.SendSingleSmsInput(
|
||||
message=message,
|
||||
receptor=phone,
|
||||
line_number='90002930',
|
||||
send_date='',
|
||||
client_reference_id=''
|
||||
)
|
||||
)
|
||||
|
||||
# response = sms_api.send_otp_sms(otp_input)
|
||||
|
||||
if response['statusCode'] == 200:
|
||||
return Response({'detail': 'OTP sent successfully'}, status=status.HTTP_200_OK)
|
||||
else:
|
||||
print('remmber to remove #TODO')
|
||||
return Response({'detail': f'OTP sent successfully {otp}'}, status=status.HTTP_200_OK)
|
||||
# return Response({'detail': response, 'otp_code': otp}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
except User.DoesNotExist:
|
||||
return Response({'detail': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
return Response({'detail': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({'detail': f'An error occurred: {response}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
|
||||
class CustomTokenObtainPairView(TokenObtainPairView):
|
||||
|
||||
@@ -27,7 +27,7 @@ EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD")
|
||||
DEFAULT_FROM_EMAIL = os.getenv("SECRET_KEY")
|
||||
|
||||
SECRET_KEY = os.getenv("SECRET_KEY")
|
||||
DEBUG = os.getenv("DEBUG")
|
||||
DEBUG = False
|
||||
# in production lists of allowed hosts and allowed orgins will genrate
|
||||
# in development every host and orgin will be true
|
||||
# in prodcution it will use the postgres info you enterd in .env.local
|
||||
@@ -39,18 +39,18 @@ if not DEBUG:
|
||||
f"https://{DOMAIN}",
|
||||
f"http://{DOMAIN}",
|
||||
]
|
||||
CORS_ALLOWED_ORIGINS = [f"https://{API_DOMAIN}", f"http://{API_DOMAIN}",
|
||||
f"http://{DOMAIN}", f"https://{DOMAIN}", ]
|
||||
|
||||
# CORS_ALLOWED_ORIGINS = [f"https://{API_DOMAIN}", f"http://{API_DOMAIN}",
|
||||
# f"http://{DOMAIN}", f"https://{DOMAIN}", ]
|
||||
CORS_ALLOW_ALL_ORIGINS = True
|
||||
# database postgres
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': os.getenv("DB_NAME"),
|
||||
'USER': os.getenv("DB_USER"),
|
||||
'PASSWORD': os.getenv("DB_PASSWORD"),
|
||||
'HOST': os.getenv("DB_HOST"),
|
||||
'PORT': os.getenv("DB_PORT"),
|
||||
'NAME': "hshop",
|
||||
'USER': "byeto",
|
||||
'PASSWORD': "vuhbyq-cypMu0-sirbon",
|
||||
'HOST': 'db',
|
||||
'PORT': 5432,
|
||||
}
|
||||
}
|
||||
else:
|
||||
|
||||
@@ -14,3 +14,37 @@ sms_api = Ghasedak(api_key=os.getenv("SMS_API_KEY"))
|
||||
### ارسال پیام otp
|
||||
|
||||
#response = sms_api.send_otp_sms(receptor='09359****', message='OTP message')
|
||||
|
||||
|
||||
|
||||
import ghasedak_sms
|
||||
|
||||
# Initialize the Ghasedak API client with your API key
|
||||
sms_api = ghasedak_sms.Ghasedak(api_key="Your_API_KEY")
|
||||
|
||||
# Define the OTP code and the recipient's phone number
|
||||
otp_code = "123456" # Replace with the generated OTP code
|
||||
phone_number = "09xxxxxxxxx" # Replace with the recipient's phone number
|
||||
|
||||
# Create the OTP input object
|
||||
otp_input = ghasedak_sms.SendOtpInput(
|
||||
send_date=None, # Immediate send; use a specific datetime for scheduled send
|
||||
receptors=[
|
||||
ghasedak_sms.SendOtpReceptorDto(
|
||||
mobile=phone_number,
|
||||
# client_reference_id='optional_client_ref_id' # Optional: Add if you have a client reference ID
|
||||
)
|
||||
],
|
||||
template_name="YourTemplateName", # Replace with your OTP template name
|
||||
inputs=[
|
||||
ghasedak_sms.SendOtpInput.OtpInput(param="Code", value=otp_code),
|
||||
# Add more parameters if your template requires them
|
||||
],
|
||||
udh=False # Set to True if you need User Data Header; typically False for standard SMS
|
||||
)
|
||||
|
||||
# Send the OTP SMS
|
||||
response = sms_api.send_otp_sms(otp_input)
|
||||
|
||||
# Print the response to check the result
|
||||
print(response)
|
||||
+18
-10
@@ -1,11 +1,13 @@
|
||||
services:
|
||||
# frontend:
|
||||
# build:
|
||||
# context: ./frontend
|
||||
# ports:
|
||||
# - "80:3000"
|
||||
# depends_on:
|
||||
# - django
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
ports:
|
||||
- "80:3000"
|
||||
depends_on:
|
||||
- django
|
||||
networks:
|
||||
- default
|
||||
|
||||
django:
|
||||
build:
|
||||
@@ -18,22 +20,28 @@ services:
|
||||
- ./backend:/app
|
||||
- media_data:/app/media
|
||||
command: ["sh", "-c", "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"]
|
||||
|
||||
networks:
|
||||
- default
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_DB: shop_db
|
||||
POSTGRES_DB: hshop
|
||||
POSTGRES_USER: byeto
|
||||
POSTGRES_PASSWORD: vuhbyq-cypMu0-sirbon
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5434:5432"
|
||||
networks:
|
||||
- default
|
||||
|
||||
|
||||
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
media_data:
|
||||
|
||||
|
||||
networks:
|
||||
default:
|
||||
@@ -120,14 +120,60 @@
|
||||
--breakpoint-xs: 480px;
|
||||
|
||||
/* ANIMATIONS */
|
||||
<<<<<<< HEAD
|
||||
--animate-marquee: marquee 3s linear infinite;
|
||||
--animate-slide-down: slideDown 300ms ease-out;
|
||||
--animate-slide-up: slideUp 300ms ease-out;
|
||||
--animate-overlay-show: overlayShow 150ms ease-in;
|
||||
--animate-content-show: contentShow 150ms ease-in;
|
||||
=======
|
||||
--animate-marquee: marquee 25s linear infinite;
|
||||
--animate-marquee-reverse: marquee 25s linear infinite reverse;
|
||||
>>>>>>> be4fa509843c81855f5ffc118150196c94a7b17b
|
||||
|
||||
@keyframes marquee {
|
||||
to {
|
||||
transform: translateX(50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
height: 0;
|
||||
}
|
||||
to {
|
||||
height: var(--reka-accordion-content-height);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
height: var(--reka-accordion-content-height);
|
||||
}
|
||||
to {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes overlayShow {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes contentShow {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -48%) scale(0.96);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* CONTAINER */
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
// types
|
||||
|
||||
type Highlight = {
|
||||
icon: string;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
// state
|
||||
|
||||
const highlights = ref<Highlight[]>([
|
||||
{
|
||||
icon: "ci:headset",
|
||||
title: "خدمات مشتری",
|
||||
description: "پشتیبانی استثنایی، راهحلهای پایدار",
|
||||
},
|
||||
{
|
||||
icon: "ci:delivery",
|
||||
title: "ارسال سریع و رایگان",
|
||||
description: "ارسال رایگان برای سفارشهای بالای ۱۵۰ دلار",
|
||||
},
|
||||
{
|
||||
icon: "ci:users",
|
||||
title: "معرفی به دوستان",
|
||||
description: "دوستان خود را معرفی کنید و هر دو ۱۵٪ تخفیف بگیرید",
|
||||
},
|
||||
{
|
||||
icon: "ci:shield-done",
|
||||
title: "پرداخت امن",
|
||||
description: "اطلاعات پرداخت شما بهصورت امن پردازش میشود",
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="w-full flex-center py-[5rem] gap-[1.25rem] container">
|
||||
<template v-for="(highlight, index) in highlights" :key="index">
|
||||
<div class="flex flex-col-center gap-[.75rem] w-1/4 px-5">
|
||||
<div class="size-[1.5rem] flex-center">
|
||||
<Icon :name="highlight.icon" />
|
||||
</div>
|
||||
<div class="w-full flex-col-center gap-[.25rem]">
|
||||
<span class="typo-sub-h-md text-black text-center">
|
||||
{{ highlight.title }}
|
||||
</span>
|
||||
<p class="text-slate-500 typo-p-md text-center">
|
||||
{{ highlight.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-[1px] h-[5rem] bg-slate-200"
|
||||
v-if="index + 1 != highlights.length"
|
||||
/>
|
||||
</template>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -27,7 +27,7 @@ const onSwiper = (swiper: SwiperClass) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full flex flex-col gap-[4rem] py-[5rem] container">
|
||||
<section class="w-full flex flex-col gap-[4rem] py-[5rem] container">
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<span class="text-black typo-h-3">
|
||||
{{ title }}
|
||||
@@ -90,7 +90,7 @@ const onSwiper = (swiper: SwiperClass) => {
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div class="w-full flex flex-col">
|
||||
<AccordionRoot
|
||||
class="w-full last:border-b last:border-slate-200"
|
||||
default-value="item-1"
|
||||
type="single"
|
||||
:collapsible="true"
|
||||
>
|
||||
<AccordionItem value="item-1" class="overflow-hidden">
|
||||
<AccordionHeader
|
||||
class="border-t border-slate-200 py-[1.5rem] flex justify-between items-center"
|
||||
>
|
||||
<span class="typo-sub-h-md text-black">مشخصات</span>
|
||||
<AccordionTrigger class="group">
|
||||
<Icon
|
||||
name="ci:plus"
|
||||
size="24"
|
||||
class="group-data-[state=open]:rotate-45 transition-transform"
|
||||
/>
|
||||
</AccordionTrigger>
|
||||
</AccordionHeader>
|
||||
<AccordionContent
|
||||
class="data-[state=open]:animate-slide-down pb-[1.5rem] data-[state=closed]:animate-slide-up overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="w-full grid grid-cols-2 gap-y-[1.5rem] gap-x-[1rem]"
|
||||
>
|
||||
<div
|
||||
v-for="i in 4"
|
||||
class="flex flex-col gap-y-[1.5rem]"
|
||||
>
|
||||
<span
|
||||
class="typo-sub-h-lg text-black w-full pt-[1.5rem]"
|
||||
>صفحه نمایش</span
|
||||
>
|
||||
<ul class="list-disc w-full ps-5">
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="item-2" class="overflow-hidden">
|
||||
<AccordionHeader
|
||||
class="border-t border-slate-200 py-[1.5rem] flex justify-between items-center"
|
||||
>
|
||||
<span class="typo-sub-h-md text-black">مشخصات</span>
|
||||
<AccordionTrigger class="group">
|
||||
<Icon
|
||||
name="ci:plus"
|
||||
size="24"
|
||||
class="group-data-[state=open]:rotate-45 transition-transform"
|
||||
/>
|
||||
</AccordionTrigger>
|
||||
</AccordionHeader>
|
||||
<AccordionContent
|
||||
class="data-[state=open]:animate-slide-down pb-[1.5rem] data-[state=closed]:animate-slide-up overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="w-full grid grid-cols-2 gap-y-[1.5rem] gap-x-[1rem]"
|
||||
>
|
||||
<div
|
||||
v-for="i in 4"
|
||||
class="flex flex-col gap-y-[1.5rem]"
|
||||
>
|
||||
<span
|
||||
class="typo-sub-h-lg text-black w-full pt-[1.5rem]"
|
||||
>صفحه نمایش</span
|
||||
>
|
||||
<ul class="list-disc w-full ps-5">
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="item-3" class="overflow-hidden">
|
||||
<AccordionHeader
|
||||
class="border-t border-slate-200 py-[1.5rem] flex justify-between items-center"
|
||||
>
|
||||
<span class="typo-sub-h-md text-black">مشخصات</span>
|
||||
<AccordionTrigger class="group">
|
||||
<Icon
|
||||
name="ci:plus"
|
||||
size="24"
|
||||
class="group-data-[state=open]:rotate-45 transition-transform"
|
||||
/>
|
||||
</AccordionTrigger>
|
||||
</AccordionHeader>
|
||||
<AccordionContent
|
||||
class="data-[state=open]:animate-slide-down pb-[1.5rem] data-[state=closed]:animate-slide-up overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="w-full grid grid-cols-2 gap-y-[1.5rem] gap-x-[1rem]"
|
||||
>
|
||||
<div
|
||||
v-for="i in 4"
|
||||
class="flex flex-col gap-y-[1.5rem]"
|
||||
>
|
||||
<span
|
||||
class="typo-sub-h-lg text-black w-full pt-[1.5rem]"
|
||||
>صفحه نمایش</span
|
||||
>
|
||||
<ul class="list-disc w-full ps-5">
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</AccordionRoot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<section class="w-full p-[5rem] flex flex-col gap-y-[1.5rem]">
|
||||
<div class="w-full flex">
|
||||
<span class="text-black typo-h-3"> جزيات محصول </span>
|
||||
</div>
|
||||
<div class="w-full flex items-start justify-between gap-[3rem]">
|
||||
<div class="w-8/12">
|
||||
<Accordion />
|
||||
</div>
|
||||
<div class="w-4/12">
|
||||
<div
|
||||
class="w-full bg-slate-50 rounded-xl flex-col-center p-[5rem] gap-[1.5rem]"
|
||||
>
|
||||
<span>داخل جعبه چیه؟</span>
|
||||
<div
|
||||
class="w-full grid grid-cols-2 gap-y-[1.5rem] gap-x-[3rem]"
|
||||
>
|
||||
<div
|
||||
v-for="i in 4"
|
||||
class="w-full flex-col-center gap-[.75rem]"
|
||||
>
|
||||
<div
|
||||
class="size-[6.25rem] rounded-full border-slate-200 bg-white flex-center"
|
||||
>
|
||||
<Icon name="ci:flag" size="44" />
|
||||
</div>
|
||||
<span class="text-black typo-p-md">Headphones</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped></style>
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div class="h-[95svh] w-full relative bg-black mt-[5rem]">
|
||||
<section class="h-[95svh] w-full relative bg-black mt-[5rem]">
|
||||
<img src="/img/product-3.jpg" class="object-cover absolute size-full" />
|
||||
<div class="size-full absolute inset-0 bg-black/60" />
|
||||
<StickyCard
|
||||
@@ -11,5 +11,5 @@
|
||||
title="نام محصول"
|
||||
class="absolute right-6 bottom-6"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,78 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<DialogRoot modal>
|
||||
<DialogTrigger>
|
||||
<Button
|
||||
end-icon="ci:filter"
|
||||
variant="secondary"
|
||||
class="rounded-full"
|
||||
>
|
||||
فیلتر محصولات
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogPortal to="#teleports">
|
||||
<DialogOverlay
|
||||
class="bg-black/60 backdrop-blur-sm data-[state=open]:animate-overlay-show fixed inset-0 z-30"
|
||||
/>
|
||||
<DialogContent
|
||||
class="data-[state=open]:animate-content-show absolute top-1/2 left-1/2 max-h-[85vh] w-[90vw] max-w-[450px] -translate-x-1/2 -translate-y-1/2 rounded-[6px] bg-white p-[25px] shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] focus:outline-none z-[100]"
|
||||
>
|
||||
<DialogTitle
|
||||
class="flex items-center typo-h-4 text-black font-semibold"
|
||||
>
|
||||
Edit profile
|
||||
</DialogTitle>
|
||||
<DialogDescription
|
||||
class="text-mauve11 mt-[10px] mb-5 text-sm leading-normal"
|
||||
>
|
||||
Make changes to your profile here. Click save when you're
|
||||
done.
|
||||
</DialogDescription>
|
||||
<fieldset class="mb-[15px] flex items-center gap-5">
|
||||
<label
|
||||
class="text-black w-[90px] text-right text-sm"
|
||||
for="name"
|
||||
>
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
class="text-black bg-stone-50 shadow-green7 focus:shadow-gray-100 inline-flex h-[35px] w-full flex-1 items-center justify-center rounded-lg px-[10px] text-sm leading-none shadow-[0_0_0_1px] outline-none focus:shadow-[0_0_0_2px]"
|
||||
defaultValue="Pedro Duarte"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset class="mb-[15px] flex items-center gap-5">
|
||||
<label
|
||||
class="text-black w-[90px] text-right text-sm"
|
||||
for="username"
|
||||
>
|
||||
Username
|
||||
</label>
|
||||
<input
|
||||
id="username"
|
||||
class="text-black bg-stone-50 shadow-green7 focus:shadow-gray-100 inline-flex h-[35px] w-full flex-1 items-center justify-center rounded-lg px-[10px] text-sm leading-none shadow-[0_0_0_1px] outline-none focus:shadow-[0_0_0_2px]"
|
||||
defaultValue="@peduarte"
|
||||
/>
|
||||
</fieldset>
|
||||
<div class="mt-[25px] flex justify-end">
|
||||
<DialogClose as-child>
|
||||
<button
|
||||
class="bg-green4 text-green11 text-sm hover:bg-green5 focus:shadow-green7 inline-flex h-[35px] items-center justify-center rounded-lg px-[15px] font-semibold leading-none focus:shadow-[0_0_0_2px] focus:outline-none"
|
||||
>
|
||||
Save changes
|
||||
</button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
<DialogClose
|
||||
class="text-black hover:bg-green4 focus:shadow-green7 absolute top-[10px] right-[10px] inline-flex h-[25px] w-[25px] appearance-none items-center justify-center rounded-full focus:shadow-[0_0_0_2px] focus:outline-none"
|
||||
aria-label="Close"
|
||||
>
|
||||
<Icon name="ci:close" />
|
||||
</DialogClose>
|
||||
</DialogContent>
|
||||
</DialogPortal>
|
||||
</DialogRoot>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM node:20-alpine as build-stage
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine as production-stage
|
||||
WORKDIR /app
|
||||
COPY --from=build-stage /app /app
|
||||
EXPOSE 3000
|
||||
ENV NODE_ENV=production
|
||||
CMD ["npm", "run", "start"]
|
||||
@@ -11,11 +11,17 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full flex flex-col-center persian-number font-yekan-bakh" dir="rtl">
|
||||
<div
|
||||
class="w-full flex flex-col-center persian-number font-yekan-bakh"
|
||||
dir="rtl"
|
||||
>
|
||||
<Header />
|
||||
<main class="w-full overflow-x-hidden">
|
||||
<slot />
|
||||
</main>
|
||||
<div class="w-full flex-col flex">
|
||||
<ServiceHighlights />
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div class="w-full flex flex-col">
|
||||
<ProductHero />
|
||||
<Video />
|
||||
<Comments />
|
||||
<ProductVideo />
|
||||
<ProductComments />
|
||||
<ProductDetails />
|
||||
<RelatedProducts title="محصولات مشابه" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div class="w-full container flex flex-col">
|
||||
<div class="w-full flex justify-between items-center py-[5rem]">
|
||||
<div class="flex flex-col items-start gap-[1.5rem] text-black">
|
||||
<div class="flex-center gap-[.75rem]">
|
||||
<span>خانه</span>
|
||||
<span>/</span>
|
||||
<span>محصولات</span>
|
||||
<span>/</span>
|
||||
<span>همه</span>
|
||||
</div>
|
||||
<h1 class="typo-hero-2">همه محصولات</h1>
|
||||
</div>
|
||||
|
||||
<FilterButton />
|
||||
</div>
|
||||
<ul class="w-full grid grid-cols-3 gap-[1.5rem]">
|
||||
<li v-for="i in 9" :key="i">
|
||||
<ProductCard
|
||||
brand="Samsung"
|
||||
title="Galaxy S20 Ultra"
|
||||
picture="/assets/img/product-1.jpg"
|
||||
:colors="['#0000ff', '#00ff00', 'red']"
|
||||
:price="599"
|
||||
:rate="2.4"
|
||||
tag="New"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user