Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add history transaction #8

Merged
merged 13 commits into from
Dec 2, 2024
Merged
4 changes: 3 additions & 1 deletion app/Http/Controllers/CartController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Illuminate\Support\Carbon;

class CartController extends Controller
{
Expand Down Expand Up @@ -94,6 +95,7 @@ function checkout(Request $request)
}

$transaction->total_price = $total_price;
$transaction->created_at = Carbon::now('Asia/Jakarta');
$transaction->save();

for($i = 0; $i < count($validatedCart['product_id']); $i++) {
Expand All @@ -105,7 +107,7 @@ function checkout(Request $request)

DB::commit();

return redirect()->back();
return redirect()->intended(route('transaction.index'));
} catch (\Exception $e) {
DB::rollBack();
throw $e;
Expand Down
49 changes: 49 additions & 0 deletions app/Http/Controllers/TransactionController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace App\Http\Controllers;

use App\Models\Transaction;
use App\Models\Category;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;

class TransactionController extends Controller
{
public function index(Request $request)
{
$categoryIds = $request->query('category_id', []);
$startDate = $request->query('start_date');
$endDate = $request->query('end_date');

$category = Category::all();

$transaction = Transaction::with(['cart_product.product.category'])
->whereHas('cart_product.product.category', function ($query) use ($categoryIds) {
if (!empty($categoryIds)) {
$query->whereIn('category_id', $categoryIds);
}
})
->whereHas('cart_product', function ($query) {
$query->where('user_id', Auth::id());
});

if ($startDate && $endDate) {
$transaction = $transaction->whereBetween('created_at', [
Carbon::parse($startDate)->startOfDay(),
Carbon::parse($endDate)->endOfDay()
]);
}

$transaction = $transaction->orderBy('created_at', 'desc')->get();

return Inertia::render('Transaction/Index', [
'transaction' => $transaction,
'category' => $category,
'categoryIds' => $categoryIds,
'startDate' => $startDate,
'endDate' => $endDate,
]);
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-popover": "^1.1.2",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"lodash.get": "^4.4.2",
"lucide-react": "^0.451.0",
"react-datepicker": "^7.5.0",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion resources/js/Components/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const AccordionTrigger = React.forwardRef<
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline",
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline hover:cursor-pointer",
className,
)}
{...props}
Expand Down
7 changes: 5 additions & 2 deletions resources/js/Components/Badge.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import * as React from "react";
import Typography from "./Typography";

export default function Badge({ children }: { children: React.ReactNode }) {
export default function Badge({
children,
className,
}: { children: React.ReactNode; className?: string }) {
return (
<div className="py-1 px-3 bg-secondary-500 border-primary-500 border rounded-lg">
<Typography variant="s3" className="">
<Typography variant="s3" className={className}>
{children}
</Typography>
</div>
Expand Down
59 changes: 59 additions & 0 deletions resources/js/Components/DatepickerFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
import RangeDatePicker from "./Forms/RangeDatepicker";

export type PopupFilterProps = {
date: Date[];
setDate: React.Dispatch<React.SetStateAction<Date[]>>;
onResetFilter?: () => void;
} & React.ComponentPropsWithoutRef<"div">;

type FormData = {
filter: Date[];
};

export default function DatepickerFilter({
date,
setDate,
onResetFilter,
}: PopupFilterProps) {
//#region //*=========== Form ===========t
const methods = useForm<FormData>({
mode: "onTouched",
defaultValues: {
filter: date,
},
});
const { control, setValue } = methods;

const filter =
useWatch({
control,
name: "filter",
}) ?? [];
//#endregion //*======== Form ===========

React.useEffect(() => {
setDate(filter);
}, [filter]);

const resetFilter = () => {
onResetFilter?.();
setValue("filter", []);
};

return (
<FormProvider {...methods}>
<div className="relative w-80">
<RangeDatePicker
id="filter"
label={null}
placeholder="Filter by date"
containerClassName="w-full"
isClearable={filter.length > 0}
onClearDate={resetFilter}
/>
</div>
</FormProvider>
);
}
22 changes: 13 additions & 9 deletions resources/js/Components/Forms/RangeDatepicker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import clsx from "clsx";
import get from "lodash.get";
import { Calendar } from "lucide-react";
import { Calendar, XCircle } from "lucide-react";
import { Controller, RegisterOptions, useFormContext } from "react-hook-form";

import "react-datepicker/dist/react-datepicker.css";
Expand All @@ -16,12 +16,12 @@ type ReactDatePickerProps = {
placeholder?: string;
defaultYear?: number;
defaultMonth?: number;
defaultValue?: string;
helperText?: string;
readOnly?: boolean;
/** Disable error style (not disabling error validation) */
hideError?: boolean;
containerClassName?: string;
onClearDate?: () => void;
} & Omit<DatePickerProps, "onChange">;

export default function RangeDatePicker({
Expand All @@ -31,11 +31,12 @@ export default function RangeDatePicker({
placeholder,
defaultYear,
defaultMonth,
defaultValue,
helperText,
readOnly = false,
hideError = false,
disabled,
isClearable,
onClearDate,
containerClassName,
}: ReactDatePickerProps) {
const {
Expand Down Expand Up @@ -76,7 +77,6 @@ export default function RangeDatePicker({
<Controller
control={control}
rules={validation}
defaultValue={defaultValue}
name={id}
render={({ field: { onChange, onBlur, value } }) => {
const startDate = Array.isArray(value)
Expand All @@ -100,7 +100,7 @@ export default function RangeDatePicker({
endDate={endDate}
className={clsx(
"flex w-full rounded-lg shadow-sm",
"min-h-[2.25rem] py-0 md:min-h-[2.5rem]",
"min-h-[2.5rem] py-0 md:min-h-[2.75rem]",
"border-gray-300 focus:border-primary-500 focus:ring-primary-500",
(readOnly || disabled) &&
"cursor-not-allowed border-gray-300 bg-gray-100 focus:border-gray-300 focus:ring-0",
Expand All @@ -118,10 +118,14 @@ export default function RangeDatePicker({
disabled={disabled}
selectsRange
/>
<Calendar
size={18}
className="pointer-events-none absolute right-4 top-1/2 -translate-y-1/2 transform text-typo-icons"
/>
<div className="absolute flex gap-2 right-4 top-1/2 -translate-y-1/2 transform text-typo-icons">
{isClearable && (
<button type="button" onClick={() => onClearDate?.()}>
<XCircle size={16} className="text-typo-icons" />
</button>
)}
<Calendar size={18} />
</div>
</div>
{helperText && (
<Typography variant="c1" color="secondary" className="mt-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,43 @@ export type PopupFilterProps<T extends Record<string, string[]>> = {
name: string;
}[];
}[];
filterQuery: T;
setFilterQuery: React.Dispatch<React.SetStateAction<T>>;
onResetFilter?: () => void;
title?: string;
} & React.ComponentPropsWithoutRef<"div">;

type FormData = {
filter: string[];
};

export default function PopupFilter<T extends Record<string, string[]>>({
filterOption,
filterQuery,
setFilterQuery,
onResetFilter,
title = "Filter",
}: PopupFilterProps<T>) {
//#region //*=========== Form ===========
const methods = useForm({
const defaultFilterValues = React.useMemo(() => {
return Object.entries(filterQuery).reduce((acc, [key, values]) => {
return [...acc, ...values.map((value) => `${key}.${value}`)];
}, [] as string[]);
}, [filterQuery]);

const methods = useForm<FormData>({
mode: "onTouched",
defaultValues: {
filter: defaultFilterValues,
},
});
const { control, setValue } = methods;

const filter: string[] = useWatch({
control,
name: "filter[]",
});
const filter =
useWatch({
control,
name: "filter",
}) ?? [];
//#endregion //*======== Form ===========

React.useEffect(() => {
Expand All @@ -56,7 +74,10 @@ export default function PopupFilter<T extends Record<string, string[]>>({
setFilterQuery(parsedFilter);
}, [filter, filterOption, setFilterQuery]);

const resetFilter = () => setValue("filter[]", []);
const resetFilter = () => {
onResetFilter?.();
setValue("filter", []);
};

return (
<Popover>
Expand Down
9 changes: 8 additions & 1 deletion resources/js/Components/UnstyledLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ export type UnstyledLinkProps = {

const UnstyledLink = React.forwardRef<HTMLAnchorElement, UnstyledLinkProps>(
(
{ children, href, openNewTab, className, inertiaLinkProps, ...rest },
{
children,
href,
openNewTab = false,
className,
inertiaLinkProps,
...rest
},
ref,
) => {
const isNewTab =
Expand Down
8 changes: 8 additions & 0 deletions resources/js/Layouts/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ export default function Navbar() {
>
Cart
</NavLink>
)}{" "}
{auth.user !== null && auth.user.role === "user" && (
<NavLink
href={route("transaction.index")}
active={route().current("transaction.index")}
>
Transaction
</NavLink>
)}
</div>
</div>
Expand Down
8 changes: 7 additions & 1 deletion resources/js/Pages/Cart/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ const CartIndex = ({ cart }: { cart: CartType[] }) => {
const readyToCheckout =
cart.filter((item) => checkedOut.includes(item.id.toString())) ?? [];

transform((data) => ({ ...data, ...getValues() }));
transform((data) => ({
...data,
product_id:
typeof getValues("product_id") === "string"
? [getValues("product_id")]
: getValues("product_id"),
}));

const onSubmit: SubmitHandler<CheckoutProductType> = async () => {
post(route("cart.checkout"), {
Expand Down
Loading