Init(Core): add to repo and add seeders
This commit is contained in:
7
resources/js/Components/ApplicationLogo.jsx
Normal file
7
resources/js/Components/ApplicationLogo.jsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function ApplicationLogo(props) {
|
||||
return (
|
||||
<svg {...props} viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
5
resources/js/Components/Button.jsx
Normal file
5
resources/js/Components/Button.jsx
Normal file
@@ -0,0 +1,5 @@
|
||||
export default function Button({ onClick, children, className, disabled }) {
|
||||
return (
|
||||
<button disabled={ disabled } onClick={ onClick } className={"rounded-lg shadow-sm children-white p-2 active:outline active:animate-pulse " + className}>{children}</button>
|
||||
);
|
||||
}
|
||||
10
resources/js/Components/CategoryAddItemButton.jsx
Normal file
10
resources/js/Components/CategoryAddItemButton.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function CategoryAddItemButton({ onClick }){
|
||||
return (
|
||||
<button className="flex flex-col items-center h-full bg-gray-200 p-6 rounded-lg">
|
||||
<span className="text-lg">افزودن محصول</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" className="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
|
||||
</svg>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
22
resources/js/Components/CategoryCard.jsx
Normal file
22
resources/js/Components/CategoryCard.jsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import CategoryItemCard from "@/Components/CategoryItemCard"
|
||||
|
||||
export default function CategoryCard({ category }){
|
||||
return (
|
||||
<div className="flex bg-white rounded-lg col-span-2 lg:col-span-1 h-full p-6 justify-between">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xl font-semibold">{category.title}</span>
|
||||
<span className="text-gray-600">{category.products.length } محصول</span>
|
||||
<span className="text-gray-600">{category.description}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
{category.products[0] ? <CategoryItemCard item={ category.products[0] } /> : null}
|
||||
<button className="flex flex-col items-center h-full bg-gray-200 p-6 rounded-lg z-40">
|
||||
<span className="text-lg">افزودن محصول</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" className="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
38
resources/js/Components/CategoryForm.jsx
Normal file
38
resources/js/Components/CategoryForm.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import Input from '@/Components/Input'
|
||||
import TextArea from '@/Components/TextArea'
|
||||
import Button from '@/Components/Button'
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
import { router } from '@inertiajs/react'
|
||||
|
||||
export default function CategoryForm({ onCancel, onSubmit }) {
|
||||
let [title, setTitle] = useState();
|
||||
let [description, setDescription] = useState();
|
||||
|
||||
let [step, setStep] = useState(1);
|
||||
|
||||
|
||||
function handleSubmit(){
|
||||
let data = {
|
||||
title: title,
|
||||
description: description,
|
||||
}
|
||||
router.post('/categories/store', data);
|
||||
onCancel();
|
||||
}
|
||||
if(step == 1){
|
||||
return (
|
||||
<div className="bg-white rounded-lg w-full max-w-lg h-fit p-8 space-y-4">
|
||||
<span className="pb-4 text-lg font-semibold">دسته بندی جدید</span>
|
||||
<Input label="عنوان" className="w-full ml-6" onChange={(e) => {setTitle(e.target.value)}} />
|
||||
<TextArea label="توضیحات (اختیاری)" className="" onChange={(e) => {setDescription(e.target.value)}}/>
|
||||
<div className="flex">
|
||||
<Button onClick={ onCancel } className="bg-white text-black border-gray-300 border-2 w-full transition hover:bg-zinc-100 ml-4 shadow-none">لغو</Button>
|
||||
<Button onClick={ e => setStep(2) } className="w-full text-white bg-zinc-800 transition hover:bg-zinc-700">ایجاد</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return("step2");
|
||||
}
|
||||
9
resources/js/Components/CategoryItemCard.jsx
Normal file
9
resources/js/Components/CategoryItemCard.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
export default function CategoryCard({ item, h }){
|
||||
console.log(item);
|
||||
return (
|
||||
<div className={"ml-[-52px] w-32 h-full first:scale-[90%] z-30 my-auto bg-red-300 rounded-lg transition duration-600 hover:z-50 hover:scale-100"}>
|
||||
{item.title}
|
||||
<img src={item.images[0] ? item.images[0].thumbnail : null} alt={item.images[0] ? item.images[0].alt : null} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
12
resources/js/Components/Checkbox.jsx
Normal file
12
resources/js/Components/Checkbox.jsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export default function Checkbox({ className = '', ...props }) {
|
||||
return (
|
||||
<input
|
||||
{...props}
|
||||
type="checkbox"
|
||||
className={
|
||||
'rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500 ' +
|
||||
className
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
11
resources/js/Components/CreateButton.jsx
Normal file
11
resources/js/Components/CreateButton.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
export default function CreateButton({ onClick, title }){
|
||||
return (
|
||||
|
||||
<button onClick={ onClick } className="bg-zinc-900 text-white rounded-lg w-fit h-full py-2 px-6 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" className="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
|
||||
</svg>
|
||||
<span className="pr-4 leading-none">{ title }</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
15
resources/js/Components/DangerButton.jsx
Normal file
15
resources/js/Components/DangerButton.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
export default function DangerButton({ className = '', disabled, children, ...props }) {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={
|
||||
`inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150 ${
|
||||
disabled && 'opacity-25'
|
||||
} ` + className
|
||||
}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
91
resources/js/Components/Dropdown.jsx
Normal file
91
resources/js/Components/Dropdown.jsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { useState, createContext, useContext, Fragment } from 'react';
|
||||
import { Link } from '@inertiajs/react';
|
||||
import { Transition } from '@headlessui/react';
|
||||
|
||||
const DropDownContext = createContext();
|
||||
|
||||
const Dropdown = ({ children }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const toggleOpen = () => {
|
||||
setOpen((previousState) => !previousState);
|
||||
};
|
||||
|
||||
return (
|
||||
<DropDownContext.Provider value={{ open, setOpen, toggleOpen }}>
|
||||
<div className="relative">{children}</div>
|
||||
</DropDownContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const Trigger = ({ children }) => {
|
||||
const { open, setOpen, toggleOpen } = useContext(DropDownContext);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div onClick={toggleOpen}>{children}</div>
|
||||
|
||||
{open && <div className="fixed inset-0 z-40" onClick={() => setOpen(false)}></div>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Content = ({ align = 'right', width = '48', contentClasses = 'py-1 bg-white', children }) => {
|
||||
const { open, setOpen } = useContext(DropDownContext);
|
||||
|
||||
let alignmentClasses = 'origin-top';
|
||||
|
||||
if (align === 'left') {
|
||||
alignmentClasses = 'origin-top-left left-0';
|
||||
} else if (align === 'right') {
|
||||
alignmentClasses = 'origin-top-right right-0';
|
||||
}
|
||||
|
||||
let widthClasses = '';
|
||||
|
||||
if (width === '48') {
|
||||
widthClasses = 'w-48';
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
show={open}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div
|
||||
className={`absolute z-50 mt-2 rounded-md shadow-lg ${alignmentClasses} ${widthClasses}`}
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<div className={`rounded-md ring-1 ring-black ring-opacity-5 ` + contentClasses}>{children}</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const DropdownLink = ({ className = '', children, ...props }) => {
|
||||
return (
|
||||
<Link
|
||||
{...props}
|
||||
className={
|
||||
'block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out ' +
|
||||
className
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
Dropdown.Trigger = Trigger;
|
||||
Dropdown.Content = Content;
|
||||
Dropdown.Link = DropdownLink;
|
||||
|
||||
export default Dropdown;
|
||||
7
resources/js/Components/Header.jsx
Normal file
7
resources/js/Components/Header.jsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function Header(){
|
||||
return (
|
||||
<div className="bg-white h-16 w-full rounded-lg">
|
||||
Hello
|
||||
</div>
|
||||
)
|
||||
}
|
||||
22
resources/js/Components/ImageInput.jsx
Normal file
22
resources/js/Components/ImageInput.jsx
Normal file
@@ -0,0 +1,22 @@
|
||||
export default function TextArea({ image, onChange, id, label, className, placeholder=null }) {
|
||||
return (
|
||||
<div className={"flex flex-col h-full" + " " + className}>
|
||||
<label className="pr-3 pb-0.5">{ label }</label>
|
||||
<div class="flex items-center justify-center w-full h-full">
|
||||
<label for="dropzone-file" class="flex flex-col items-center justify-center w-full h-full border border-gray-300 rounded-lg cursor-pointer">
|
||||
<div class="w-full h-full flex flex-col items-center justify-center p-6">
|
||||
{ image ? image : (
|
||||
<div className="flex justify-center items-center bg-gray-200 rounded-lg w-full h-52">
|
||||
<svg class="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 16">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
<span className="pt-4">برای آپلود عکس کلیک کنید</span>
|
||||
</div>
|
||||
<input onChange={ onChange } id="dropzone-file" type="file" class="hidden" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
resources/js/Components/Input.jsx
Normal file
9
resources/js/Components/Input.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
export default function Input({ error, onChange, id, label, className, placeholder=null, value }) {
|
||||
return (
|
||||
<div className={"flex flex-col" + " " + className}>
|
||||
<label className="pr-3 pb-0.5">{ label }</label>
|
||||
<input onChange={ onChange } id={id} type="text" className="w-full border-gray-300 rounded-lg" placeholder={placeholder} value={value}/>
|
||||
{error && <span className="text-sm text-red-600 pt-0.5 pr-3">{error}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
7
resources/js/Components/InputError.jsx
Normal file
7
resources/js/Components/InputError.jsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function InputError({ message, className = '', ...props }) {
|
||||
return message ? (
|
||||
<p {...props} className={'text-sm text-red-600 ' + className}>
|
||||
{message}
|
||||
</p>
|
||||
) : null;
|
||||
}
|
||||
7
resources/js/Components/InputLabel.jsx
Normal file
7
resources/js/Components/InputLabel.jsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function InputLabel({ value, className = '', children, ...props }) {
|
||||
return (
|
||||
<label {...props} className={`block font-medium text-sm text-gray-700 ` + className}>
|
||||
{value ? value : children}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
57
resources/js/Components/Modal.jsx
Normal file
57
resources/js/Components/Modal.jsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Fragment } from 'react';
|
||||
import { Dialog, Transition } from '@headlessui/react';
|
||||
|
||||
export default function Modal({ children, show = false, maxWidth = '2xl', closeable = true, onClose = () => {} }) {
|
||||
const close = () => {
|
||||
if (closeable) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const maxWidthClass = {
|
||||
sm: 'sm:max-w-sm',
|
||||
md: 'sm:max-w-md',
|
||||
lg: 'sm:max-w-lg',
|
||||
xl: 'sm:max-w-xl',
|
||||
'2xl': 'sm:max-w-2xl',
|
||||
}[maxWidth];
|
||||
|
||||
return (
|
||||
<Transition show={show} as={Fragment} leave="duration-200">
|
||||
<Dialog
|
||||
as="div"
|
||||
id="modal"
|
||||
className="fixed inset-0 flex overflow-y-auto px-4 py-6 sm:px-0 items-center z-50 transform transition-all"
|
||||
onClose={close}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gray-500/75" />
|
||||
</Transition.Child>
|
||||
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel
|
||||
className={`mb-6 bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full sm:mx-auto ${maxWidthClass}`}
|
||||
>
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
18
resources/js/Components/NavLink.jsx
Normal file
18
resources/js/Components/NavLink.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Link } from '@inertiajs/react';
|
||||
|
||||
export default function NavLink({ active = false, className = '', children, ...props }) {
|
||||
return (
|
||||
<Link
|
||||
{...props}
|
||||
className={
|
||||
'inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium leading-5 transition duration-150 ease-in-out focus:outline-none ' +
|
||||
(active
|
||||
? 'border-indigo-400 text-gray-900 focus:border-indigo-700 '
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:text-gray-700 focus:border-gray-300 ') +
|
||||
className
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
15
resources/js/Components/PrimaryButton.jsx
Normal file
15
resources/js/Components/PrimaryButton.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
export default function PrimaryButton({ className = '', disabled, children, ...props }) {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={
|
||||
`inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150 ${
|
||||
disabled && 'opacity-25'
|
||||
} ` + className
|
||||
}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
88
resources/js/Components/ProductForm.jsx
Normal file
88
resources/js/Components/ProductForm.jsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import Input from '@/Components/Input'
|
||||
import TextArea from '@/Components/TextArea'
|
||||
import Button from '@/Components/Button'
|
||||
import ImageInput from '@/Components/ImageInput'
|
||||
import SelectInput from '@/Components/SelectInput'
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
import { router } from '@inertiajs/react'
|
||||
|
||||
export default function ProductForm({ onCancel, onSubmit, categories, preselectedCategory=null }) {
|
||||
|
||||
categories = categories.map((category) => ({value: category.id, label: category.title}));
|
||||
|
||||
let [title, setTitle] = useState();
|
||||
let [description, setDescription] = useState();
|
||||
let [price, setPrice] = useState(0);
|
||||
let [inventory, setInventory] = useState(-1);
|
||||
|
||||
let [selectedImage, setSelectedImage] = useState(null);
|
||||
|
||||
let [createCategoryOpen, setCreateCategoryOpen] = useState(false);
|
||||
let [selectedCategory, setSelectedCategory] = useState(preselectedCategory ?? (categories[0] ?? null));
|
||||
|
||||
function handleSubmit(){
|
||||
let data = {
|
||||
title: title,
|
||||
description: description,
|
||||
price: price,
|
||||
inventory: inventory,
|
||||
image: selectedImage,
|
||||
}
|
||||
console.log(selectedCategory);
|
||||
router.post('/products/store/'+String(selectedCategory.value), data);
|
||||
onCancel();
|
||||
}
|
||||
|
||||
function handleCreateCategoryOpen(inputValue){
|
||||
setCategoryTitle(inputValue);
|
||||
setCreateCategoryOpen(true);
|
||||
}
|
||||
|
||||
|
||||
function handleModalCancel(){
|
||||
setCreateCategoryOpen(false);
|
||||
setCreateTagOpen(false);
|
||||
}
|
||||
|
||||
function handleCategoryModalSubmit(){
|
||||
setCreateCategoryOpen(false);
|
||||
setData('category', {value: categoryTitle, label: categoryTitle});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col bg-white rounded-lg w-full max-w-4xl h-fit p-8 space-y-8">
|
||||
<span className="pb-4 text-lg font-semibold">محصول جدید</span>
|
||||
<SelectInput value={ selectedCategory } setValue={e => setSelectedCategory(e) } options={ categories } onCreateOption={ handleCreateCategoryOpen } label="دسته بندی" className="w-full"/>
|
||||
<div className="flex flex-col lg:flex-row">
|
||||
<div className="w-full pl-4 flex flex-col space-y-4">
|
||||
<Input label="عنوان" className="w-full ml-6" onChange={(e) => {setTitle(e.target.value)}} />
|
||||
<div className="flex justify-between">
|
||||
<Input label="قیمت" className="w-full ml-6" onChange={(e) => {setPrice(e.target.value)}} />
|
||||
<Input label="موجودی" className="w-full" onChange={(e) => {setInventory(e.target.value)}} />
|
||||
</div>
|
||||
<TextArea label="توضیحات (اختیاری)" className="" onChange={(e) => {setDescription(e.target.value)}}/>
|
||||
</div>
|
||||
<div className="w-full pr-4 h-full">
|
||||
<ImageInput id="image" image={selectedImage && (
|
||||
<div className="flex flex-col justify-center w-full h-52 rounded-2xl">
|
||||
<img
|
||||
alt="not found"
|
||||
className="h-52 object-scale-down"
|
||||
src={URL.createObjectURL(selectedImage)}
|
||||
/>
|
||||
<button class="z-10" onClick={() => setSelectedImage(null)}>حذف</button>
|
||||
</div>
|
||||
)} onChange={(event) => {
|
||||
setSelectedImage(event.target.files[0]);
|
||||
}} label="تصویر" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button onClick={ onCancel } className="bg-white text-black border-gray-300 border-2 w-full transition hover:bg-zinc-100 ml-4 shadow-none">لغو</Button>
|
||||
<Button onClick={ handleSubmit } className="w-full text-white bg-zinc-800 transition hover:bg-zinc-700">ایجاد</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
57
resources/js/Components/ProductTable.jsx
Normal file
57
resources/js/Components/ProductTable.jsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Link } from '@inertiajs/react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { router } from '@inertiajs/react'
|
||||
|
||||
export default function ProductTable({ items, title, links}) {
|
||||
console.log(items);
|
||||
const items_list = items.map(items => {
|
||||
return (
|
||||
<tr key={items.id} className="border-y text-right font-normal">
|
||||
<td className="py-2">{ items.id }</td>
|
||||
<td className="py-2">{ items.images[0] ? <img className="z-0 w-16 h-16 rounded-lg shadow-inner p-1" src={ items.images[0].thumbnail } alt={ items.images[0].alt } /> : null }</td>
|
||||
<td className="py-2 text-lg">{ items.title }</td>
|
||||
<td className="py-2">{ items.price }</td>
|
||||
<td className="py-2">{ items.inventory }</td>
|
||||
</tr>
|
||||
)}
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<div className="h-full">
|
||||
<div className="flex flex-col bg-white p-4 rounded-lg shadow-md hover:shadow-lg transition overflow-auto max-h-full">
|
||||
<div className="w-full px-3">
|
||||
<table className="table-auto border-collapse w-full text-right">
|
||||
<th className="text-sm font-normal w-12">
|
||||
<span>
|
||||
آیدی
|
||||
</span>
|
||||
</th>
|
||||
<th className="text-sm font-normal w-20">
|
||||
<span>
|
||||
عکس
|
||||
</span>
|
||||
</th>
|
||||
<th className="text-sm font-normal">
|
||||
<span>
|
||||
عنوان
|
||||
</span>
|
||||
</th>
|
||||
<th className="text-sm font-normal">
|
||||
<span>
|
||||
قیمت
|
||||
</span>
|
||||
</th>
|
||||
<th className="text-sm font-normal">
|
||||
<span>
|
||||
موجودی
|
||||
</span>
|
||||
</th>
|
||||
|
||||
{items_list}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
16
resources/js/Components/ResponsiveNavLink.jsx
Normal file
16
resources/js/Components/ResponsiveNavLink.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Link } from '@inertiajs/react';
|
||||
|
||||
export default function ResponsiveNavLink({ active = false, className = '', children, ...props }) {
|
||||
return (
|
||||
<Link
|
||||
{...props}
|
||||
className={`w-full flex items-start pl-3 pr-4 py-2 border-l-4 ${
|
||||
active
|
||||
? 'border-indigo-400 text-indigo-700 bg-indigo-50 focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700'
|
||||
: 'border-transparent text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300'
|
||||
} text-base font-medium focus:outline-none transition duration-150 ease-in-out ${className}`}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
12
resources/js/Components/Searchbar.jsx
Normal file
12
resources/js/Components/Searchbar.jsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export default function Searchbar({ onSearch, value }) {
|
||||
|
||||
return (
|
||||
<div class="relative h-fit">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
||||
<svg aria-hidden="true" class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
|
||||
</div>
|
||||
<input type="search" id="default-search" class="block w-52 p-2 pr-10 pl-2 rounded-lg border-gray-200 focus:border-red-200 focus:ring-red-200" placeholder="جستجو..." />
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
16
resources/js/Components/SecondaryButton.jsx
Normal file
16
resources/js/Components/SecondaryButton.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
export default function SecondaryButton({ type = 'button', className = '', disabled, children, ...props }) {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
type={type}
|
||||
className={
|
||||
`inline-flex items-center px-4 py-2 bg-white border border-gray-300 rounded-md font-semibold text-xs text-gray-700 uppercase tracking-widest shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 transition ease-in-out duration-150 ${
|
||||
disabled && 'opacity-25'
|
||||
} ` + className
|
||||
}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
13
resources/js/Components/SelectInput.jsx
Normal file
13
resources/js/Components/SelectInput.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import CreatableSelect from 'react-select/creatable';
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function Input({ options, value, setValue, onCreateOption, id, label, className, placeholder=null, isMulti=false}) {
|
||||
|
||||
return (
|
||||
<div className={"relative flex flex-col" + " " + className}>
|
||||
<label className="pr-3 pb-0.5">{ label }</label>
|
||||
<CreatableSelect isClearable value={ value } onChange={(newValue) => setValue(newValue)} onCreateOption={(e) => onCreateOption(e) } formatCreateLabel={(inputValue) => "ایجاد " + label + ' "' + inputValue + '"'} placeholder={placeholder} options={options} isMulti={isMulti}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
17
resources/js/Components/Sidebar.jsx
Normal file
17
resources/js/Components/Sidebar.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import SidebarItem from '@/Components/SidebarItem'
|
||||
|
||||
export default function Sidebar() {
|
||||
return (
|
||||
<div className="flex flex-col w-60 h-full bg-white">
|
||||
<div className="p-6 border-b text-2xl">
|
||||
<span>پنل مدیریت</span>
|
||||
</div>
|
||||
<div>
|
||||
<SidebarItem title="دشبورد" href="/dashboard" />
|
||||
<SidebarItem title="محصولات" href="/products" />
|
||||
<SidebarItem title="دسته بندی ها" href="/categories" />
|
||||
<SidebarItem title="مشخصه ها" href="/properties" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
8
resources/js/Components/SidebarItem.jsx
Normal file
8
resources/js/Components/SidebarItem.jsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Link } from '@inertiajs/react'
|
||||
export default function SidebarItem({ title, href }) {
|
||||
return (
|
||||
<div className="flex border-b hover:border-l hover:border-l-black transition duration-600 hover:bg-gradient-to-r from-gray-200 to-gray-50">
|
||||
<Link className="w-full h-full p-4" href={href}>{title}</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
8
resources/js/Components/TextArea.jsx
Normal file
8
resources/js/Components/TextArea.jsx
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function TextArea({ onChange, id, label, className, placeholder=null }) {
|
||||
return (
|
||||
<div className={"flex flex-col" + " " + className}>
|
||||
<label className="pr-3 pb-0.5">{ label }</label>
|
||||
<textarea onChange={ onChange } id={id} type="text" placeholder={placeholder} className="w-full border-gray-300 rounded-lg"></textarea>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
23
resources/js/Components/TextInput.jsx
Normal file
23
resources/js/Components/TextInput.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { forwardRef, useEffect, useRef } from 'react';
|
||||
|
||||
export default forwardRef(function TextInput({ type = 'text', className = '', isFocused = false, ...props }, ref) {
|
||||
const input = ref ? ref : useRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (isFocused) {
|
||||
input.current.focus();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<input
|
||||
{...props}
|
||||
type={type}
|
||||
className={
|
||||
'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm ' +
|
||||
className
|
||||
}
|
||||
ref={input}
|
||||
/>
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user