sina_sajjadi
4 weeks ago
128 changed files with 1970 additions and 1092 deletions
-
45.env.template
-
0README.md
-
2next.config.js
-
2package.json
-
3public/locales/en/common.json
-
17public/locales/en/form.json
-
45src/components/auth/forget-password/enter-email-view.tsx
-
17src/components/auth/forget-password/enter-new-password-view.tsx
-
191src/components/auth/forget-password/forget-password.tsx
-
72src/components/auth/login-form.tsx
-
320src/components/auth/registration-form.tsx
-
39src/components/common/get-file-url.tsx
-
50src/components/common/uploader.tsx
-
52src/components/dashboard/admin.tsx
-
42src/components/dashboard/widgets/box/widget-order-by-status.tsx
-
15src/components/filters/category-type-filter.tsx
-
14src/components/icons/social/facebook.tsx
-
5src/components/icons/social/index.tsx
-
10src/components/icons/social/twitter.tsx
-
2src/components/layouts/app.tsx
-
22src/components/layouts/navigation/authorized-menu.tsx
-
4src/components/layouts/navigation/sidebar-item.tsx
-
12src/components/layouts/navigation/top-navbar.tsx
-
3src/components/layouts/shop/index.tsx
-
36src/components/layouts/topbar/visit-store.tsx
-
26src/components/order/order-list.tsx
-
93src/components/product/form-utils.ts
-
7src/components/product/product-category-input.tsx
-
105src/components/product/product-form.tsx
-
80src/components/product/product-list.tsx
-
26src/components/product/product-simple-form.tsx
-
4src/components/product/product-tag-input.tsx
-
150src/components/product/product-validation-schema.ts
-
1src/components/shop/approve-shop-view.tsx
-
272src/components/shop/shop-form.tsx
-
119src/components/shop/shop-validation-schema.ts
-
3src/components/ui/button.tsx
-
194src/components/ui/field-array.tsx
-
2src/components/ui/input.tsx
-
2src/components/ui/lang-action/action.tsx
-
76src/components/ui/switch-input.tsx
-
2src/components/ui/text-area.tsx
-
4src/components/user/user-details.tsx
-
65src/components/validations/shop-validation-schema .tsx
-
2src/components/widgets/sticker-card.tsx
-
4src/config/routes.ts
-
5src/contexts/settings.context.tsx
-
7src/data/category.ts
-
30src/data/client/api-endpoints.ts
-
1src/data/client/category.ts
-
18src/data/client/curd-factory.ts
-
25src/data/client/http-client.ts
-
11src/data/client/product.ts
-
18src/data/client/settings.ts
-
4src/data/client/shop.ts
-
48src/data/client/upload.ts
-
6src/data/client/user.ts
-
2src/data/dashboard.ts
-
2src/data/order.ts
-
19src/data/product.ts
-
147src/data/settings.ts
-
32src/data/shop.ts
-
4src/data/tag.ts
-
18src/data/upload.ts
-
50src/data/user.ts
-
4src/pages/_app.tsx
-
6src/pages/_document.tsx
-
28src/pages/index.tsx
-
7src/pages/logout.tsx
-
24src/pages/orders/index.tsx
-
3src/pages/products/[productSlug]/[action].tsx
-
45src/pages/products/create.tsx
-
9src/pages/products/index.tsx
-
6src/pages/register.tsx
-
0src/pages/shop/attributes/[attributeId]/[action].tsx
-
0src/pages/shop/attributes/create.tsx
-
0src/pages/shop/attributes/index.tsx
-
0src/pages/shop/authors/create.tsx
-
0src/pages/shop/authors/index.tsx
-
0src/pages/shop/coupons/[couponSlug]/[action].tsx
-
0src/pages/shop/coupons/create.tsx
-
0src/pages/shop/coupons/index.tsx
-
6src/pages/shop/create.tsx
-
13src/pages/shop/edit.tsx
-
0src/pages/shop/faqs/[id]/[action].tsx
-
0src/pages/shop/faqs/create.tsx
-
0src/pages/shop/faqs/index.tsx
-
0src/pages/shop/flash-sale/[slug]/index.tsx
-
0src/pages/shop/flash-sale/index.tsx
-
0src/pages/shop/flash-sale/my-products.tsx
-
0src/pages/shop/flash-sale/vendor-request/[id]/[action].tsx
-
0src/pages/shop/flash-sale/vendor-request/[id]/index.tsx
-
0src/pages/shop/flash-sale/vendor-request/create.tsx
-
0src/pages/shop/flash-sale/vendor-request/index.tsx
-
21src/pages/shop/index.tsx
-
0src/pages/shop/manufacturers/create.tsx
-
0src/pages/shop/manufacturers/index.tsx
-
0src/pages/shop/orders/[orderId]/index.tsx
-
0src/pages/shop/orders/index.tsx
-
0src/pages/shop/orders/transaction.tsx
@ -1,45 +0,0 @@ |
|||
# Don't add `/` after the URL |
|||
NEXT_PUBLIC_REST_API_ENDPOINT="http://localhost" |
|||
NEXT_PUBLIC_SHOP_URL="http://localhost:3003" |
|||
|
|||
# Application Mode and Authentication key |
|||
# development or production |
|||
APPLICATION_MODE=production |
|||
|
|||
|
|||
NEXT_PUBLIC_AUTH_TOKEN_KEY=AUTH_CRED |
|||
|
|||
# Default Language |
|||
NEXT_PUBLIC_DEFAULT_LANGUAGE=en |
|||
|
|||
# Multilang |
|||
# If you want to enable multilang then follow this doc -> https://chawkbazar-doc.vercel.app/multilingual |
|||
NEXT_PUBLIC_ENABLE_MULTI_LANG=false |
|||
NEXT_PUBLIC_AVAILABLE_LANGUAGES=en,de |
|||
|
|||
# API Key for third party service |
|||
NEXT_PUBLIC_GOOGLE_MAP_API_KEY= |
|||
|
|||
# pusher config |
|||
# 'log', 'pusher', 'null', 'redis' |
|||
NEXT_PUBLIC_API_BROADCAST_DRIVER=pusher |
|||
# true or false |
|||
NEXT_PUBLIC_PUSHER_DEV_MOOD=true |
|||
NEXT_PUBLIC_PUSHER_APP_KEY='4f67daf566b719c6f606' |
|||
NEXT_PUBLIC_PUSHER_APP_CLUSTER='ap2' |
|||
NEXT_PUBLIC_BROADCAST_AUTH_URL="${NEXT_PUBLIC_REST_API_ENDPOINT}/broadcasting/auth" |
|||
|
|||
# Channel name from PHP |
|||
NEXT_PUBLIC_STORE_NOTICE_CREATED_CHANNEL_PRIVATE=private-store_notice.created |
|||
NEXT_PUBLIC_ORDER_CREATED_CHANNEL_PRIVATE=private-order.created |
|||
NEXT_PUBLIC_MESSAGE_CHANNEL_PRIVATE=private-message.created |
|||
|
|||
# Event name from PHP |
|||
NEXT_PUBLIC_STORE_NOTICE_CREATED_EVENT=store.notice.event |
|||
NEXT_PUBLIC_ORDER_CREATED_EVENT=order.create.event |
|||
NEXT_PUBLIC_MESSAGE_EVENT=message.event |
|||
|
|||
|
|||
# App version |
|||
|
|||
NEXT_PUBLIC_VERSION="6.6.0" |
@ -0,0 +1,39 @@ |
|||
import { HttpClient } from "@/data/client/http-client"; |
|||
|
|||
const getFileURL = async (file: File) => { |
|||
let image; |
|||
if (file) { |
|||
const modifiedFileName = file.name.replaceAll(" ", ""); |
|||
const modifiedFile = new File([file], modifiedFileName, { |
|||
type: file.type, |
|||
lastModified: file.lastModified, |
|||
}); |
|||
|
|||
const formData = new FormData(); |
|||
formData.append("file", modifiedFile); |
|||
console.log("gfdsgfdsgfsd"); |
|||
|
|||
try { |
|||
const response = await HttpClient.post( |
|||
'https://mesbahi.nwhco.ir/api/upload-tmp-media/', // Full URL
|
|||
formData, |
|||
{ |
|||
headers: { |
|||
"Content-Type": "multipart/form-data", |
|||
"X-CSRFToken": "58B9R5uED9steCGYg4A4hOB2dt6BZ5lT52R08lk6cIcOhgWiMNRRdKgJ6WXhhQMb", // Include CSRF token
|
|||
}, |
|||
} |
|||
); |
|||
if (response.status === 200) { |
|||
image = response.data; |
|||
} else { |
|||
console.log("Something went wrong during file upload"); |
|||
} |
|||
} catch (error: any) { |
|||
console.log(error.response?.data || error.message); // Log the actual error response
|
|||
} |
|||
} |
|||
return image; |
|||
}; |
|||
|
|||
export default getFileURL; |
@ -1,9 +1,5 @@ |
|||
export const FacebookIcon: React.FC<React.SVGAttributes<{}>> = (props) => ( |
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" {...props}> |
|||
<path |
|||
data-name="_ionicons_svg_logo-facebook (6)" |
|||
d="M11.338 0H.662A.663.663 0 000 .663v10.674a.663.663 0 00.662.662H6V7.25H4.566V5.5H6V4.206a2.28 2.28 0 012.459-2.394c.662 0 1.375.05 1.541.072V3.5H8.9c-.753 0-.9.356-.9.881V5.5h1.794L9.56 7.25H8V12h3.338a.663.663 0 00.662-.663V.662A.663.663 0 0011.338 0z" |
|||
fill="currentColor" |
|||
/> |
|||
</svg> |
|||
); |
|||
|
|||
export const TelegramIcon: React.FC<React.SVGAttributes<{}>> = (props) => ( |
|||
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="#b5b5b5" stroke="#b5b5b5"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title>telegram_line</title> <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Brand" transform="translate(-672.000000, -0.000000)"> <g id="telegram_line" transform="translate(672.000000, 0.000000)"> <path d="M24,0 L24,24 L0,24 L0,0 L24,0 Z M12.5934901,23.257841 L12.5819402,23.2595131 L12.5108777,23.2950439 L12.4918791,23.2987469 L12.4918791,23.2987469 L12.4767152,23.2950439 L12.4056548,23.2595131 C12.3958229,23.2563662 12.3870493,23.2590235 12.3821421,23.2649074 L12.3780323,23.275831 L12.360941,23.7031097 L12.3658947,23.7234994 L12.3769048,23.7357139 L12.4804777,23.8096931 L12.4953491,23.8136134 L12.4953491,23.8136134 L12.5071152,23.8096931 L12.6106902,23.7357139 L12.6232938,23.7196733 L12.6232938,23.7196733 L12.6266527,23.7031097 L12.609561,23.275831 C12.6075724,23.2657013 12.6010112,23.2592993 12.5934901,23.257841 L12.5934901,23.257841 Z M12.8583906,23.1452862 L12.8445485,23.1473072 L12.6598443,23.2396597 L12.6498822,23.2499052 L12.6498822,23.2499052 L12.6471943,23.2611114 L12.6650943,23.6906389 L12.6699349,23.7034178 L12.6699349,23.7034178 L12.678386,23.7104931 L12.8793402,23.8032389 C12.8914285,23.8068999 12.9022333,23.8029875 12.9078286,23.7952264 L12.9118235,23.7811639 L12.8776777,23.1665331 C12.8752882,23.1545897 12.8674102,23.1470016 12.8583906,23.1452862 L12.8583906,23.1452862 Z M12.1430473,23.1473072 C12.1332178,23.1423925 12.1221763,23.1452606 12.1156365,23.1525954 L12.1099173,23.1665331 L12.0757714,23.7811639 C12.0751323,23.7926639 12.0828099,23.8018602 12.0926481,23.8045676 L12.108256,23.8032389 L12.3092106,23.7104931 L12.3186497,23.7024347 L12.3186497,23.7024347 L12.3225043,23.6906389 L12.340401,23.2611114 L12.337245,23.2485176 L12.337245,23.2485176 L12.3277531,23.2396597 L12.1430473,23.1473072 Z" id="MingCute" fill-rule="nonzero"> </path> <path d="M21.8394,6.05639 C22.0315,4.8917 20.8652,3.97177 19.7773,4.42984 L2.67703,11.63 C1.48461,12.132 1.42351,13.8558 2.67788,14.3758 C3.60596,14.7605 5.02633,15.3246 6.45085,15.7943 C7.61932,16.1795 8.8931,16.5371 9.91353,16.6383 C10.1929,16.9725 10.5445,17.2935 10.9017,17.5872 C11.4487,18.0371 12.1074,18.5012 12.7873,18.9455 C14.1489,19.8352 15.6597,20.6865 16.678,21.2396 C17.8949,21.9006 19.3517,21.1395 19.5705,19.8131 L21.8394,6.05639 Z M4.59485,12.9925 L19.7186,6.62459 L17.6009,19.4649 C16.6024,18.9219 15.163,18.1087 13.8813,17.2713 C13.2329,16.8475 12.6407,16.4279 12.172,16.0425 C12.0051,15.9052 11.8638,15.7802 11.7461,15.6683 L15.7072,11.7071 C16.0977,11.3166 16.0977,10.6834 15.7072,10.2929 C15.3167,9.90237 14.6835,9.90237 14.293,10.2929 L9.95476,14.6311 C9.22132,14.5373 8.19888,14.2647 7.07709,13.8949 C6.21377,13.6102 5.34574,13.2869 4.59485,12.9925 Z" id="形状" fill="#09244B"> </path> </g> </g> </g> </g></svg> |
|||
|
|||
); |
@ -1,4 +1,3 @@ |
|||
export { FacebookIcon } from './facebook'; |
|||
export { TelegramIcon } from './facebook'; |
|||
export { InstagramIcon } from './instagram'; |
|||
export { TwitterIcon } from './twitter'; |
|||
export { YouTubeIcon } from './youtube'; |
|||
export { WhatsappIcon } from './twitter'; |
@ -1,9 +1,3 @@ |
|||
export const TwitterIcon: React.FC<React.SVGAttributes<{}>> = (props) => ( |
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14.747 12" {...props}> |
|||
<path |
|||
data-name="_ionicons_svg_logo-twitter (5)" |
|||
d="M14.747 1.422a6.117 6.117 0 01-1.737.478A3.036 3.036 0 0014.341.225a6.012 6.012 0 01-1.922.734 3.025 3.025 0 00-5.234 2.069 2.962 2.962 0 00.078.691A8.574 8.574 0 011.026.553a3.032 3.032 0 00.941 4.044 2.955 2.955 0 01-1.375-.378v.037A3.028 3.028 0 003.02 7.225a3.046 3.046 0 01-.8.106 2.854 2.854 0 01-.569-.056 3.03 3.03 0 002.828 2.1 6.066 6.066 0 01-3.759 1.3 6.135 6.135 0 01-.722-.044A8.457 8.457 0 004.631 12a8.557 8.557 0 008.616-8.619c0-.131 0-.262-.009-.391a6.159 6.159 0 001.509-1.568z" |
|||
fill="currentColor" |
|||
/> |
|||
</svg> |
|||
export const WhatsappIcon: React.FC<React.SVGAttributes<{}>> = (props) => ( |
|||
<svg viewBox="0 0 24 24" id="meteor-icon-kit__solid-whatsapp" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill-rule="evenodd" clip-rule="evenodd" d="M20.5129 3.4866C18.2882 1.24722 15.2597 -0.00837473 12.1032 4.20445e-05C5.54964 4.20445e-05 0.216056 5.33306 0.213776 11.8883C0.210977 13.9746 0.75841 16.0247 1.80085 17.8319L0.114014 23.9932L6.41672 22.34C8.15975 23.2898 10.1131 23.7874 12.0981 23.7874H12.1032C18.6556 23.7874 23.9897 18.4538 23.992 11.8986C24.0022 8.74248 22.7494 5.71347 20.5129 3.4866ZM17.5234 14.3755C17.2264 14.2267 15.7659 13.5085 15.4934 13.4064C15.2209 13.3044 15.0231 13.2576 14.8253 13.5552C14.6275 13.8528 14.058 14.5215 13.8847 14.7199C13.7114 14.9182 13.5381 14.9427 13.241 14.794C12.944 14.6452 11.9869 14.3316 10.8519 13.3198C9.96884 12.5319 9.36969 11.5594 9.19867 11.2618C9.02765 10.9642 9.18043 10.8057 9.32922 10.6552C9.46261 10.5224 9.62622 10.3086 9.77444 10.1348C9.92266 9.9609 9.97283 9.83776 10.0714 9.63938C10.1701 9.44099 10.121 9.26769 10.0469 9.1189C9.97283 8.97011 9.37824 7.50788 9.13083 6.9133C8.88969 6.3341 8.64513 6.4122 8.46271 6.40023C8.29169 6.39168 8.09102 6.38997 7.89264 6.38997C7.58822 6.39793 7.30097 6.53267 7.10024 6.76166C6.82831 7.05923 6.061 7.77752 6.061 9.23976C6.061 10.702 7.12532 12.1146 7.27354 12.313C7.42176 12.5114 9.36855 15.5117 12.3472 16.7989C12.9004 17.0375 13.4657 17.2468 14.0409 17.426C14.7523 17.654 15.3999 17.6204 15.9118 17.544C16.4819 17.4585 17.6694 16.8251 17.9173 16.1313C18.1653 15.4376 18.1648 14.8424 18.0884 14.7187C18.012 14.595 17.8204 14.5266 17.5234 14.3778V14.3755Z" fill="#758CA3"></path></g></svg> |
|||
); |
@ -0,0 +1,194 @@ |
|||
import React, { useState, useEffect } from 'react'; |
|||
import { socialIcon } from '@/settings/site.settings'; |
|||
import { getIcon } from '@/utils/get-icon'; |
|||
import * as socialIcons from '@/components/icons/social'; |
|||
import Select from 'react-select'; |
|||
import Input from './input'; |
|||
import Button from '@/components/ui/button'; |
|||
|
|||
export const FieldArray = ({ data, setData }) => { |
|||
const [fields, setFields] = useState(data || [{ platform: null, url: '' }]); // Initialize with data from parent
|
|||
|
|||
const customOption = (props) => { |
|||
const { data, innerRef, innerProps } = props; |
|||
return ( |
|||
<div |
|||
ref={innerRef} |
|||
{...innerProps} |
|||
className="flex items-center space-x-2 px-3 py-2 hover:bg-gray-100 cursor-pointer" |
|||
> |
|||
<span className="flex items-center justify-center w-6 h-6"> |
|||
{getIcon({ |
|||
iconList: socialIcons, |
|||
iconName: data.value, |
|||
className: 'w-5 h-5 text-gray-500', |
|||
})} |
|||
</span> |
|||
<span className="text-gray-800 text-sm">{data.label}</span> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const options = socialIcon.map((item) => ({ |
|||
value: item.value, |
|||
label: item.label, |
|||
})); |
|||
|
|||
// Filter out already selected platforms from options
|
|||
const getAvailableOptions = (index) => { |
|||
const selectedValues = fields |
|||
.filter((_, i) => i !== index && fields[i].platform) // Get selected platforms except current field
|
|||
.map((field) => field.platform.value); // Extract selected platform values
|
|||
|
|||
return options.filter((option) => !selectedValues.includes(option.value)); // Exclude selected platforms
|
|||
}; |
|||
|
|||
const customStyles = { |
|||
control: (base) => ({ |
|||
...base, |
|||
minHeight: '47px', |
|||
height: '47px', |
|||
padding: '0 10px', |
|||
display: 'flex', |
|||
alignItems: 'center', |
|||
}), |
|||
valueContainer: (base) => ({ |
|||
...base, |
|||
display: 'flex', |
|||
alignItems: 'center', |
|||
height: '100%', |
|||
padding: '0 8px', |
|||
}), |
|||
input: (base) => ({ |
|||
...base, |
|||
margin: '0', |
|||
padding: '0', |
|||
height: '100%', |
|||
}), |
|||
singleValue: (base) => ({ |
|||
...base, |
|||
display: 'flex', |
|||
alignItems: 'center', |
|||
}), |
|||
dropdownIndicator: (base) => ({ |
|||
...base, |
|||
padding: '8px', |
|||
}), |
|||
menu: (base) => ({ |
|||
...base, |
|||
marginTop: 0, |
|||
borderRadius: '4px', |
|||
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)', |
|||
border: '1px solid #e5e7eb', |
|||
}), |
|||
option: (base, { isFocused }) => ({ |
|||
...base, |
|||
display: 'flex', |
|||
alignItems: 'center', |
|||
padding: '10px 12px', |
|||
backgroundColor: isFocused ? '#f9fafb' : '#fff', |
|||
color: '#374151', |
|||
cursor: 'pointer', |
|||
}), |
|||
}; |
|||
|
|||
const handleAddField = () => { |
|||
const newFields = [...fields, { platform: null, url: '' }]; |
|||
setFields(newFields); |
|||
setData(newFields); // Pass the updated data to parent
|
|||
}; |
|||
|
|||
const handleRemoveField = (index) => { |
|||
const newFields = fields.filter((_, i) => i !== index); |
|||
setFields(newFields); |
|||
setData(newFields); // Pass the updated data to parent
|
|||
}; |
|||
|
|||
const handleFieldChange = (index, key, value) => { |
|||
const updatedFields = fields.map((field, i) => |
|||
i === index ? { ...field, [key]: value } : field, |
|||
); |
|||
setFields(updatedFields); |
|||
setData(updatedFields); // Pass the updated data to parent
|
|||
}; |
|||
|
|||
// Update parent component when fields change
|
|||
useEffect(() => { |
|||
setData(fields); // Pass the current fields data to parent
|
|||
}, [fields, setData]); |
|||
|
|||
return ( |
|||
<div className=""> |
|||
{fields.map((field, index) => ( |
|||
|
|||
<div key={index} className="flex gap-4 items-center pb-8 my-5 border-b border-gray-300 border-dashed sm:my-8"> |
|||
<div> |
|||
<label |
|||
htmlFor={`social-platform-${index}`} |
|||
className="block text-sm font-medium text-gray-700" |
|||
> |
|||
Select social platform |
|||
</label> |
|||
<Select |
|||
styles={customStyles} |
|||
options={getAvailableOptions(index)} // Use filtered options
|
|||
isSearchable={false} |
|||
value={field.platform} |
|||
onChange={(selectedOption) => |
|||
handleFieldChange(index, 'platform', selectedOption) |
|||
} |
|||
getOptionLabel={(e) => ( |
|||
<div className="flex items-center space-x-2"> |
|||
<span className="flex items-center justify-center w-6 h-6"> |
|||
{getIcon({ |
|||
iconList: socialIcons, |
|||
iconName: e.value, |
|||
className: 'w-4 h-4 text-gray-500', |
|||
})} |
|||
</span> |
|||
<span className="text-gray-800 text-sm">{e.label}</span> |
|||
</div> |
|||
)} |
|||
components={{ Option: customOption }} |
|||
className="mt-4 w-96" |
|||
id={`social-platform-${index}`} |
|||
/> |
|||
</div> |
|||
<div className='w-96'> |
|||
<label |
|||
htmlFor={`url-${index}`} |
|||
className="block text-sm font-medium text-gray-700" |
|||
> |
|||
Url <span className="text-red-500">*</span> |
|||
</label> |
|||
<Input |
|||
id={`url-${index}`} |
|||
value={field.url} |
|||
variant="outline" |
|||
onChange={(e) => handleFieldChange(index, 'url', e.target.value)} |
|||
className="mt-1" |
|||
/> |
|||
</div> |
|||
<div className="ml-10"> |
|||
<button |
|||
type="button" |
|||
onClick={() => handleRemoveField(index)} |
|||
className="text-sm text-red-500 transition-colors duration-200 hover:text-red-700 focus:outline-none sm:mt-8" |
|||
> |
|||
Remove |
|||
</button> |
|||
</div> |
|||
</div> |
|||
))} |
|||
<div> |
|||
<Button |
|||
type="button" |
|||
onClick={handleAddField} |
|||
className="w-full sm:w-auto text-sm md:text-base mt-3" |
|||
> |
|||
Add New Social Profile |
|||
</Button> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
@ -0,0 +1,65 @@ |
|||
const validateForm = (formValues) => { |
|||
const errors = {}; |
|||
// Validate shop name
|
|||
if (!formValues.title) { |
|||
errors.title = 'Shop name is required'; |
|||
} else if (formValues.title.length < 3) { |
|||
errors.title = 'Shop name must be at least 3 characters long'; |
|||
} |
|||
|
|||
// Validate slug
|
|||
if (!formValues.slug) { |
|||
errors.slug = 'Slug is required'; |
|||
} else if (!/^[a-z0-9-]+$/.test(formValues.slug)) { |
|||
errors.slug = 'Slug must contain only lowercase letters, numbers, and hyphens'; |
|||
} |
|||
|
|||
// Validate description
|
|||
if (!formValues.description) { |
|||
errors.description = 'Description is required'; |
|||
} else if (formValues.description.length < 10) { |
|||
errors.description = 'Description must be at least 10 characters long'; |
|||
} |
|||
|
|||
// Validate required file inputs (logo, cover_image)
|
|||
if (!formValues.logo) { |
|||
errors.logo = 'Shop logo is required'; |
|||
} |
|||
if (!formValues.cover_image) { |
|||
errors.cover_image = 'Cover image is required'; |
|||
} |
|||
|
|||
// Validate account holder name
|
|||
if (!formValues.account_holder_name) { |
|||
errors.account_holder_name = 'Account holder name is required'; |
|||
} |
|||
|
|||
// Validate email format for account holder email
|
|||
if (!formValues.account_holder_email) { |
|||
errors.account_holder_email = 'Account holder email is required'; |
|||
} else if (!/\S+@\S+\.\S+/.test(formValues.account_holder_email)) { |
|||
errors.account_holder_email = 'Invalid email format'; |
|||
} |
|||
|
|||
// Validate bank name
|
|||
if (!formValues.bank_name) { |
|||
errors.bank_name = 'Bank name is required'; |
|||
} |
|||
|
|||
// Validate account number
|
|||
if (!formValues.account_number) { |
|||
errors.account_number = 'Account number is required'; |
|||
} else if (!/^\d+$/.test(formValues.account_number)) { |
|||
errors.account_number = 'Account number must be digits only'; |
|||
} |
|||
|
|||
// Validate address
|
|||
if (!formValues.country) { |
|||
errors.country = 'Country is required'; |
|||
} |
|||
if (!formValues.city) { |
|||
errors.city = 'City is required'; |
|||
} |
|||
|
|||
return errors; |
|||
}; |
@ -1,22 +1,46 @@ |
|||
// uploadClient.ts
|
|||
import { HttpClient } from './http-client'; |
|||
import { API_ENDPOINTS } from './api-endpoints'; |
|||
import { Attachment } from '@/types'; |
|||
|
|||
export const uploadClient = { |
|||
upload: async (variables: any) => { |
|||
let formData = new FormData(); |
|||
|
|||
|
|||
// Create a new FormData instance to hold the files
|
|||
const formData = new FormData(); |
|||
|
|||
// Loop over the variables (which are the files) and append them to the FormData object
|
|||
variables.forEach((attachment: any) => { |
|||
formData.append('attachment[]', attachment); |
|||
// Assuming 'attachment' is a file object
|
|||
const modifiedFileName = attachment.name.replaceAll(" ", ""); |
|||
const modifiedFile = new File([attachment], modifiedFileName, { |
|||
type: attachment.type, |
|||
lastModified: attachment.lastModified, |
|||
}); |
|||
|
|||
formData.append('file', modifiedFile); // Append file to the formData
|
|||
}); |
|||
const options = { |
|||
headers: { |
|||
'Content-Type': 'multipart/form-data', |
|||
}, |
|||
}; |
|||
return HttpClient.post<Attachment>( |
|||
API_ENDPOINTS.ATTACHMENTS, |
|||
formData, |
|||
options |
|||
); |
|||
|
|||
// Get CSRF token from cookies or context if needed
|
|||
|
|||
// Set headers for the request (don't manually set 'Content-Type' as the browser handles it)
|
|||
; |
|||
|
|||
// Send the request using HttpClient and return the response data
|
|||
|
|||
try { |
|||
const response = await HttpClient.post<Attachment>(API_ENDPOINTS.ATTACHMENTS, formData); |
|||
// Check if the response is successful
|
|||
// if (response.status === 200) {
|
|||
return response; // This should return the uploaded file data or URL
|
|||
// } else {
|
|||
// throw new Error("Something went wrong during the file upload");
|
|||
// }
|
|||
|
|||
} catch (error: any) { |
|||
console.error("Error uploading files:", error.response?.data || error.message); |
|||
throw error; // Rethrow the error so react-query can handle it
|
|||
} |
|||
}, |
|||
}; |
@ -0,0 +1,45 @@ |
|||
import Layout from '@/components/layouts/admin'; |
|||
import CreateOrUpdateProductForm from '@/components/product/product-form'; |
|||
import ErrorMessage from '@/components/ui/error-message'; |
|||
import Loader from '@/components/ui/loader/loader'; |
|||
import { useProductQuery } from '@/data/product'; |
|||
import { useRouter } from 'next/router'; |
|||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; |
|||
import { useTranslation } from 'next-i18next'; |
|||
import { Config } from '@/config'; |
|||
import Link from '@/components/ui/link'; |
|||
|
|||
export default function UpdateProductPage() { |
|||
const { query, locale } = useRouter(); |
|||
const { t } = useTranslation(); |
|||
|
|||
const { |
|||
product, |
|||
isLoading: loading, |
|||
error, |
|||
} = useProductQuery({ |
|||
slug: query.productSlug as string, |
|||
language: locale!, |
|||
}); |
|||
|
|||
// if (loading) return <Loader text={t('common:text-loading')} />;
|
|||
// if (error) return <ErrorMessage message={error?.message as string} />;
|
|||
return ( |
|||
<> |
|||
<div className="flex items-center gap-5 border-b border-dashed border-border-base py-5 sm:py-8"> |
|||
<h4 className="text-lg font-semibold text-heading"> |
|||
{t('form:form-title-create-product')} |
|||
</h4> |
|||
</div> |
|||
|
|||
<CreateOrUpdateProductForm initialValues={product} /> |
|||
</> |
|||
); |
|||
} |
|||
UpdateProductPage.Layout = Layout; |
|||
|
|||
export const getServerSideProps = async ({ locale }: any) => ({ |
|||
props: { |
|||
...(await serverSideTranslations(locale, ['common', 'form'])), |
|||
}, |
|||
}); |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue