Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
340bd8c4bf | ||
|
|
88c71ccdfa | ||
|
|
5b3b9e44fa | ||
|
|
ac2ff2ec92 | ||
|
|
79e0d39280 | ||
|
|
fd9113109c | ||
|
|
da7e1067d2 | ||
|
|
75779b0373 | ||
|
|
2ec6ad5b5c | ||
|
|
2d541c71ba | ||
|
|
46f3bd6014 | ||
|
|
c374c97e66 | ||
|
|
a4ca53004f | ||
|
|
2d9473ea7c | ||
|
|
6bfa48bed8 | ||
|
|
43167fe7a1 | ||
|
|
39391e5522 | ||
|
|
ec509a7b43 | ||
|
|
a76bd89ec1 | ||
|
|
cafdb29024 | ||
|
|
d79d0d3d09 | ||
|
|
7606c5b4e2 | ||
|
|
bc9a0fd740 | ||
|
|
7f9a106fca | ||
|
|
0bcc8f7f6a | ||
|
|
31b7587571 | ||
|
|
82fb1ea2bc | ||
|
|
bd5e46e712 | ||
|
|
542469bc67 | ||
|
|
631684cb05 | ||
|
|
54f4572533 | ||
|
|
ca933f624a | ||
|
|
90ab974e03 | ||
|
|
daeeb09798 | ||
|
|
c8935b885e | ||
|
|
4ac1278a33 | ||
|
|
d20f0d89e5 | ||
|
|
515b78c57f | ||
|
|
8a53575cd1 | ||
|
|
c8b12db1b3 | ||
|
|
e3c4b7a23a | ||
|
|
41a0842026 | ||
|
|
a9164e5e5f | ||
|
|
d7e39095bb | ||
|
|
84ddf2b771 | ||
|
|
cb632adbb4 | ||
|
|
6a1fa42956 | ||
|
|
9002e6a261 | ||
|
|
254d4fe04a | ||
|
|
02e06caca1 | ||
|
|
00033b9c68 | ||
|
|
02863ebc8b | ||
|
|
a31166e3ff | ||
|
|
c7b74bc9b6 | ||
|
|
01621a8786 | ||
|
|
a07e3c21d6 | ||
|
|
fb99b41b3a | ||
|
|
183ca100dc | ||
|
|
203ee5de97 | ||
|
|
58843041db | ||
|
|
3964c22c66 | ||
|
|
ec02a4ef83 | ||
|
|
587cb9facf | ||
|
|
5c5faaf8a1 |
No files matched your search
@@ -43,3 +43,5 @@ Thumbs.db
|
|||||||
|
|
||||||
vite.config.*.timestamp*
|
vite.config.*.timestamp*
|
||||||
vitest.config.*.timestamp*
|
vitest.config.*.timestamp*
|
||||||
|
|
||||||
|
storybook-static
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import nx from '@nx/eslint-plugin';
|
||||||
|
import baseConfig from '../../eslint.config.mjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig,
|
||||||
|
...nx.configs['flat/react'],
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Backoffice</title>
|
||||||
|
<base href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
|
<link rel="stylesheet" href="/src/styles.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
'@tailwindcss/postcss': {
|
||||||
|
base: join(import.meta.dirname, '../../'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "backoffice",
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "apps/backoffice/src",
|
||||||
|
"projectType": "application",
|
||||||
|
"tags": [],
|
||||||
|
"// targets": "to see all targets run: nx show project backoffice --web",
|
||||||
|
"targets": {}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
@@ -0,0 +1,9 @@
|
|||||||
|
<svg width="317" height="170" viewBox="0 0 317 170" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<rect width="317" height="169.613" fill="url(#pattern0_164_317)"/>
|
||||||
|
<defs>
|
||||||
|
<pattern id="pattern0_164_317" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||||
|
<use xlink:href="#image0_164_317" transform="scale(0.00184502 0.00344828)"/>
|
||||||
|
</pattern>
|
||||||
|
<image id="image0_164_317" width="542" height="290" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAh4AAAEiCAYAAAC2k9sBAAAABGdBTUEAALGPC/xhBQAACklpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAAEiJnVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKLine truncated
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 351 KiB |
@@ -0,0 +1,9 @@
|
|||||||
|
<svg width="138" height="52" viewBox="0 0 138 52" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<rect width="137.505" height="51.7538" fill="url(#pattern0_164_316)"/>
|
||||||
|
<defs>
|
||||||
|
<pattern id="pattern0_164_316" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||||
|
<use xlink:href="#image0_164_316" transform="matrix(0.000440723 0 0 0.00117096 -0.173204 -1.3185)"/>
|
||||||
|
</pattern>
|
||||||
|
<image id="image0_164_316" width="3000" height="3000" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAC7gAAAu4CAYAAABxFfV5AAAACXBIWXMAAC4jAAAuIwF4pT92AAX6AElEQVR4nOzaMQEAIAzAsIF/z0MGPRIFFdCzuwMAAAAAAAAAAAAAAL/d3wEAAAAAAAAAAAAAADBjcAcAAAAAAAAAAAAAIMLgDgAAAAAAAAAAAABAgsEdAAAAAAAAAAAAAIAEgzsAAAAAAAAAAAAAAAkGdwAAAAAAAAAAAAAAEgzuAAAAAAAAAAAAAAAkGNwBAAAAAAAAAAAAAEgwuAMAAAAAAAAAAAAAkGBwBwAAAAAAAAAAAAAgweAOAAAAAAAAAAAAAECCwR0AAAAAAAAAAAAAgASDOwAAAAAAAAAAAAAACQZ3AAAALine truncated
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 511 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
import { FC, ReactElement } from 'react';
|
||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
|
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
|
||||||
|
|
||||||
|
export const AppLayout: FC = (): ReactElement => {
|
||||||
|
return (
|
||||||
|
<div className="bg-primary-50 min-h-screen flex justify-center">
|
||||||
|
<div className="bg-primary-50 min-h-screen w-full flex">
|
||||||
|
<BackofficeSidebar />
|
||||||
|
<div className="flex-1 overflow-auto lg:max-w-[1000px] mx-auto">
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppLayout;
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { FC, ReactElement } from 'react';
|
||||||
|
import {
|
||||||
|
FilterOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { Button, Input } from '@imphnen-frontend-service/ui/atoms';
|
||||||
|
import { DataTable } from '@imphnen-frontend-service/ui/organisms';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
getCoreRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
PaginationState,
|
||||||
|
useReactTable,
|
||||||
|
RowSelectionState,
|
||||||
|
} from '@tanstack/react-table';
|
||||||
|
|
||||||
|
interface Account {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
address: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock data for demonstration
|
||||||
|
const mockData: Account[] = Array.from({ length: 90 }, (_, i) => ({
|
||||||
|
id: i + 1,
|
||||||
|
name: i === 0 ? 'Ahmad Wijuana' : 'Nama Lengkap',
|
||||||
|
email: 'fullname23@gmail.com',
|
||||||
|
phone: '081904423804',
|
||||||
|
address: 'Jl. Pantai Cibaduyut Indonesia',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const columns: ColumnDef<Account>[] = [
|
||||||
|
{
|
||||||
|
id: 'select',
|
||||||
|
header: ({ table }) => (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="rounded"
|
||||||
|
checked={table.getIsAllRowsSelected()}
|
||||||
|
onChange={table.getToggleAllRowsSelectedHandler()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="rounded"
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
onChange={row.getToggleSelectedHandler()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'No',
|
||||||
|
accessorKey: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Nama Lengkap',
|
||||||
|
accessorKey: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Email',
|
||||||
|
accessorKey: 'email',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Nomor Telp',
|
||||||
|
accessorKey: 'phone',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Alamat Pengiriman',
|
||||||
|
accessorKey: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Action',
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
// handleEdit(row.id);
|
||||||
|
}}
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<EditOutlined /> Edit
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Components: FC = (): ReactElement => {
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 9,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: mockData,
|
||||||
|
columns,
|
||||||
|
state: {
|
||||||
|
pagination,
|
||||||
|
rowSelection,
|
||||||
|
},
|
||||||
|
enableRowSelection: true,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
pageCount: Math.ceil(mockData.length / pagination.pageSize),
|
||||||
|
manualPagination: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="w-full px-[48px] py-[40px] flex flex-col gap-8">
|
||||||
|
{/* Header */}
|
||||||
|
<header className="bg-white py-4 px-8 rounded-md shadow p-4">
|
||||||
|
<h1 className="text-p2 font-semibold">Data Akun</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Account Table Section */}
|
||||||
|
<section className="flex flex-col gap-6 p-8 bg-white rounded-md">
|
||||||
|
{/* Search and Filter */}
|
||||||
|
<div className="flex justify-between items-center gap-8 mb-2">
|
||||||
|
<div className="relative w-full">
|
||||||
|
<Input
|
||||||
|
placeholder="Cari berdasarkan nama lengkap, email"
|
||||||
|
className="pl-12 w-full max-h-full"
|
||||||
|
/>
|
||||||
|
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-[16px]">
|
||||||
|
<SearchOutlined />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
disabled
|
||||||
|
size="md"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<FilterOutlined />
|
||||||
|
Filters
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Table */}
|
||||||
|
<DataTable data={mockData} columns={columns} table={table} />
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Components;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { FC, ReactElement } from 'react';
|
||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
|
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
|
||||||
|
|
||||||
|
export const AppLayout: FC = (): ReactElement => {
|
||||||
|
return (
|
||||||
|
<div className="bg-primary-50 min-h-screen flex justify-center">
|
||||||
|
<div className="bg-primary-50 min-h-screen w-full flex">
|
||||||
|
<BackofficeSidebar />
|
||||||
|
<div className="flex-1 overflow-auto lg:max-w-[1000px] mx-auto">
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppLayout;
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { ArrowDownOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { Button } from '@imphnen-frontend-service/ui/atoms';
|
||||||
|
import { FC, ReactElement } from 'react';
|
||||||
|
|
||||||
|
export const Components: FC = (): ReactElement => {
|
||||||
|
return (
|
||||||
|
<main className="w-full px-[48px] py-[40px] flex flex-col gap-8">
|
||||||
|
{/* Dashboard Header */}
|
||||||
|
<header className="bg-white py-4 px-8 rounded-md shadow p-4">
|
||||||
|
<h1 className="text-p2 font-semibold">Dashboard</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="flex justify-between gap-[40px] p-8 bg-white rounded-md">
|
||||||
|
<div className="w-full flex flex-col gap-[40px]">
|
||||||
|
{/* Summary Section */}
|
||||||
|
<section>
|
||||||
|
<h2 className="text-p2 font-medium text-primary-500 mb-8">
|
||||||
|
Summary
|
||||||
|
</h2>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
{/* Summary Cards */}
|
||||||
|
{[1, 2, 3, 4].map((item) => (
|
||||||
|
<div
|
||||||
|
key={item}
|
||||||
|
className="bg-white rounded-lg shadow-sm py-4 px-6 flex items-center border border-neutral-100"
|
||||||
|
>
|
||||||
|
<div className="mr-4 text-primary-500 bg-primary-100 p-[8px] rounded-md">
|
||||||
|
<ArrowDownOutlined className="text-[20px]" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<h3 className="text-p1 font-semibold">1000</h3>
|
||||||
|
<p className="text-label1 text-neutral-500">
|
||||||
|
Total Participants
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Gacha Items Section */}
|
||||||
|
<section className="flex flex-col gap-8">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<h2 className="text-p2 font-medium text-primary-500">
|
||||||
|
Gacha Items
|
||||||
|
</h2>
|
||||||
|
<Button variant="primary" size="sm" className="items-end gap-3">
|
||||||
|
<span>Tambah Item</span>
|
||||||
|
<PlusOutlined className="text-[16px]" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Gacha Items List */}
|
||||||
|
<div className="flex flex-col gap-4 max-h-140 overflow-auto">
|
||||||
|
{[1, 2, 3, 4, 5, 6].map((item) => (
|
||||||
|
<div
|
||||||
|
key={item}
|
||||||
|
className="bg-white max-h-[80px] overflow-clip rounded-lg shadow-sm flex justify-between border border-neutral-100"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col py-4 px-6 gap-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-p3 text-primary-500 font-medium">
|
||||||
|
Lanyard IMPHNEN
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-center gap-2 text-label2 text-gray-500 mt-1">
|
||||||
|
<span>Prize {item}</span>
|
||||||
|
<span>Chance Rate: (0.1%)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-start gap-2">
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
size="sm"
|
||||||
|
className="text-[10px] text-neutral-500 p-0 font-normal hover:bg-transparent hover:text-primary-500"
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
size="sm"
|
||||||
|
className="text-[10px] text-neutral-500 p-0 font-normal hover:bg-transparent hover:text-red-500"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Lebih baik gunakan gambar yang sudah di-clip dengan size height: 78px daripada hard-code object-position dan margin */}
|
||||||
|
<img
|
||||||
|
src="gacha-clip.png"
|
||||||
|
alt="Lanyard IMPHNEN"
|
||||||
|
className="h-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right-side illustration */}
|
||||||
|
<img
|
||||||
|
src="gacha.png"
|
||||||
|
alt=""
|
||||||
|
className="rounded-lg hidden xl:block xl:min-w-[436px] h-auto object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Components;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { FC, ReactElement } from 'react';
|
||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
|
export const AppLayout: FC = (): ReactElement => {
|
||||||
|
return (
|
||||||
|
<main className="bg-primary-50 min-h-screen">
|
||||||
|
<Outlet />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppLayout;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { FC, ReactElement } from 'react';
|
||||||
|
import { useNavigate } from 'react-router';
|
||||||
|
import { Button } from '@imphnen-frontend-service/ui/atoms';
|
||||||
|
import { InputForm } from '@imphnen-frontend-service/ui/molecules';
|
||||||
|
|
||||||
|
export const Components: FC = (): ReactElement => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center items-center min-h-screen">
|
||||||
|
<div className="bg-white border border-primary-200 shadow-lg p-[60px] text-center flex flex-col justify-items-stretch gap-8 rounded-2xl">
|
||||||
|
<img src="/logos/logo.svg" alt="" className="h-[70px] w-auto" />
|
||||||
|
<h1 className="text-primary-500 text-p1 font-semibold">
|
||||||
|
Welcome to IMPHNEN Backoffice
|
||||||
|
</h1>
|
||||||
|
<InputForm
|
||||||
|
label="Email"
|
||||||
|
placeholder="Masukkan Email"
|
||||||
|
type="email"
|
||||||
|
size="lg"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
<InputForm
|
||||||
|
label="Password"
|
||||||
|
placeholder="Masukkan password"
|
||||||
|
type="password"
|
||||||
|
size="lg"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
<Button onClick={() => navigate('/dashboard')}>Login</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Components;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { FC, ReactElement } from 'react';
|
||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
|
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
|
||||||
|
|
||||||
|
export const AppLayout: FC = (): ReactElement => {
|
||||||
|
return (
|
||||||
|
<div className="bg-primary-50 min-h-screen flex justify-center">
|
||||||
|
<div className="bg-primary-50 min-h-screen w-full flex">
|
||||||
|
<BackofficeSidebar />
|
||||||
|
<div className="flex-1 overflow-auto lg:max-w-[1000px] mx-auto">
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppLayout;
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { FC, ReactElement } from 'react';
|
||||||
|
import {
|
||||||
|
FilterOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
AuditOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { Button, Input } from '@imphnen-frontend-service/ui/atoms';
|
||||||
|
import { DataTable } from '@imphnen-frontend-service/ui/organisms';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
getCoreRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
PaginationState,
|
||||||
|
useReactTable,
|
||||||
|
RowSelectionState,
|
||||||
|
} from '@tanstack/react-table';
|
||||||
|
|
||||||
|
type TransactionStatus = 'valid' | 'invalid' | 'unchecked';
|
||||||
|
|
||||||
|
interface Transaction {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
transactionNumber: string;
|
||||||
|
status: TransactionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock data for transactions
|
||||||
|
const mockTransactions: Transaction[] = Array.from({ length: 20 }, (_, i) => ({
|
||||||
|
id: i + 1,
|
||||||
|
name: i === 0 ? 'Ahmad Wijuana' : 'Nama Lengkap',
|
||||||
|
transactionNumber: '25D2133Y9AFYBD',
|
||||||
|
status: (i % 3 === 0
|
||||||
|
? 'invalid'
|
||||||
|
: i % 5 === 0
|
||||||
|
? 'unchecked'
|
||||||
|
: 'valid') as TransactionStatus,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const columns: ColumnDef<Account>[] = [
|
||||||
|
{
|
||||||
|
id: 'select',
|
||||||
|
header: ({ table }) => (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="rounded"
|
||||||
|
checked={table.getIsAllRowsSelected()}
|
||||||
|
onChange={table.getToggleAllRowsSelectedHandler()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="rounded"
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
onChange={row.getToggleSelectedHandler()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'No',
|
||||||
|
accessorKey: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Nama Lengkap',
|
||||||
|
accessorKey: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Nomor Transaksi',
|
||||||
|
accessorKey: 'transactionNumber',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Order Valid?',
|
||||||
|
accessorKey: 'status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const status = row.original.status;
|
||||||
|
const statusColors: Record<TransactionStatus, string> = {
|
||||||
|
valid: 'bg-success-500 text-white',
|
||||||
|
invalid: 'bg-danger-500 text-white',
|
||||||
|
unchecked: 'bg-yellow-400 text-black',
|
||||||
|
};
|
||||||
|
const statusText: Record<TransactionStatus, string> = {
|
||||||
|
valid: 'Valid',
|
||||||
|
invalid: 'Invalid',
|
||||||
|
unchecked: 'Unchecked',
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`py-1 px-3 rounded-md text-center ${statusColors[status]}`}
|
||||||
|
>
|
||||||
|
{statusText[status]}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Action',
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
// handleUpdate(row.id);
|
||||||
|
}}
|
||||||
|
className="flex items-center gap-2 w-full"
|
||||||
|
>
|
||||||
|
<AuditOutlined className="text-[16px]" /> Update
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Components: FC = (): ReactElement => {
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 9,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: mockTransactions,
|
||||||
|
columns,
|
||||||
|
state: {
|
||||||
|
pagination,
|
||||||
|
rowSelection,
|
||||||
|
},
|
||||||
|
enableRowSelection: true,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
pageCount: Math.ceil(mockTransactions.length / pagination.pageSize),
|
||||||
|
manualPagination: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="w-full px-[48px] py-[40px] flex flex-col gap-8">
|
||||||
|
{/* Header */}
|
||||||
|
<header className="bg-white py-4 px-8 rounded-md shadow p-4">
|
||||||
|
<h1 className="text-p2 font-semibold">Validasi Transaksi</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Account Table Section */}
|
||||||
|
<section className="flex flex-col gap-6 p-8 bg-white rounded-md">
|
||||||
|
{/* Search and Filter */}
|
||||||
|
<div className="flex justify-between items-center gap-8 mb-2">
|
||||||
|
<div className="relative w-full">
|
||||||
|
<Input
|
||||||
|
placeholder="Cari berdasarkan nama lengkap, nomor order Shopee"
|
||||||
|
className="pl-12 w-full max-h-full"
|
||||||
|
/>
|
||||||
|
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-[16px]">
|
||||||
|
<SearchOutlined />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="md"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<FilterOutlined />
|
||||||
|
Filters
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Table */}
|
||||||
|
<DataTable data={mockTransactions} columns={columns} table={table} />
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Components;
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Bai+Jamjuree:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
|
||||||
|
@import 'tailwindcss';
|
||||||
|
@source "../../../libs/ui/**/*.{ts,tsx}";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-primary-50: #f0f8ff;
|
||||||
|
--color-primary-100: #e1f0fd;
|
||||||
|
--color-primary-200: #bce1fb;
|
||||||
|
--color-primary-300: #81cbf8;
|
||||||
|
--color-primary-400: #3eb0f2;
|
||||||
|
--color-primary-500: #23a1eb;
|
||||||
|
--color-primary-600: #0877c1;
|
||||||
|
--color-primary-700: #085f9c;
|
||||||
|
--color-primary-800: #0b5181;
|
||||||
|
--color-primary-900: #0f446b;
|
||||||
|
--color-primary-950: #0a2b47;
|
||||||
|
|
||||||
|
--color-neutral-50: #f6f6f6;
|
||||||
|
--color-neutral-100: #e7e7e7;
|
||||||
|
--color-neutral-200: #d0d0d0;
|
||||||
|
--color-neutral-300: #b7b7b7;
|
||||||
|
--color-neutral-400: #9f9f9f;
|
||||||
|
--color-neutral-500: #7e7e7e;
|
||||||
|
--color-neutral-600: #5d5d5d;
|
||||||
|
--color-neutral-700: #4a4a4a;
|
||||||
|
--color-neutral-800: #3a3a3a;
|
||||||
|
--color-neutral-900: #2f2f2f;
|
||||||
|
--color-neutral-950: #2b2b2b;
|
||||||
|
|
||||||
|
--color-success-100: #e0fbd8;
|
||||||
|
--color-success-200: #bcf8b0;
|
||||||
|
--color-success-300: #8eea85;
|
||||||
|
--color-success-400: #63d564;
|
||||||
|
--color-success-500: #35ba43;
|
||||||
|
--color-success-600: #269f3e;
|
||||||
|
--color-success-700: #1a8439;
|
||||||
|
--color-success-800: #106b32;
|
||||||
|
--color-success-900: #0b592f;
|
||||||
|
|
||||||
|
--color-info-100: #ccfcfe;
|
||||||
|
--color-info-200: #9bf3fd;
|
||||||
|
--color-info-300: #67e3fb;
|
||||||
|
--color-info-400: #42cdf8;
|
||||||
|
--color-info-500: #04acf3;
|
||||||
|
--color-info-600: #0185d0;
|
||||||
|
--color-info-700: #0264af;
|
||||||
|
--color-info-800: #01478d;
|
||||||
|
--color-info-900: #003375;
|
||||||
|
|
||||||
|
--color-warning-100: #fffcd3;
|
||||||
|
--color-warning-200: #fffaa9;
|
||||||
|
--color-warning-300: #fff67d;
|
||||||
|
--color-warning-400: #fff25d;
|
||||||
|
--color-warning-500: #ffed27;
|
||||||
|
--color-warning-600: #dbc91d;
|
||||||
|
--color-warning-700: #b7a714;
|
||||||
|
--color-warning-800: #93850b;
|
||||||
|
--color-warning-900: #7a6d07;
|
||||||
|
|
||||||
|
--color-danger-100: #ffe8da;
|
||||||
|
--color-danger-200: #ffcbb3;
|
||||||
|
--color-danger-300: #ffaa8d;
|
||||||
|
--color-danger-400: #ff8870;
|
||||||
|
--color-danger-500: #ff5242;
|
||||||
|
--color-danger-600: #da3030;
|
||||||
|
--color-danger-700: #b7212d;
|
||||||
|
--color-danger-800: #93152a;
|
||||||
|
--color-danger-900: #7a0c27;
|
||||||
|
|
||||||
|
--radius-none: 0px;
|
||||||
|
--radius-sm: 2px;
|
||||||
|
--radius-md: 4px;
|
||||||
|
--radius-lg: 8px;
|
||||||
|
--radius-xl: 12px;
|
||||||
|
--radius-2xl: 16px;
|
||||||
|
--radius-3xl: 24px;
|
||||||
|
--radius-full: 50%;
|
||||||
|
|
||||||
|
--font-bai-jamjuree: 'Bai Jamjuree', sans-serif;
|
||||||
|
|
||||||
|
--text-h1: 3.833rem;
|
||||||
|
/* ~46px */
|
||||||
|
--text-h2: 3.083rem;
|
||||||
|
/* ~37px */
|
||||||
|
--text-h3: 2.417rem;
|
||||||
|
/* ~29px */
|
||||||
|
--text-p1: 1.917rem;
|
||||||
|
/* ~23px */
|
||||||
|
--text-p2: 1.583rem;
|
||||||
|
/* ~19px */
|
||||||
|
--text-p3: 1.25rem;
|
||||||
|
/* 15px */
|
||||||
|
--text-label1: 1rem;
|
||||||
|
/* 12px */
|
||||||
|
--text-label2: 0.833rem;
|
||||||
|
/* ~10px */
|
||||||
|
--text-label3: 0.677rem;
|
||||||
|
/* ~8px */
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
|
||||||
|
*,
|
||||||
|
::after,
|
||||||
|
::before,
|
||||||
|
::backdrop,
|
||||||
|
::file-selector-button {
|
||||||
|
border-color: var(--color-neutral-200, currentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom scrollbar */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--color-neutral-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #cccccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--color-neutral-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: 'Bai Jamjuree', sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import { middleware } from './middleware';
|
||||||
|
import { StrictMode } from 'react';
|
||||||
|
import { createBrowserRouter, RouteObject, RouterProvider } from 'react-router';
|
||||||
|
import {
|
||||||
|
add404PageToRoutesChildren,
|
||||||
|
addErrorElementToRoutes,
|
||||||
|
convertPagesToRoute,
|
||||||
|
QueryProvider,
|
||||||
|
} from '@imphnen-frontend-service/utils';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const files = import.meta.glob('./app/**/*(page|layout).tsx');
|
||||||
|
const errorFiles = import.meta.glob('./app/**/*error.tsx');
|
||||||
|
const notFoundFiles = import.meta.glob('./app/**/*404.tsx');
|
||||||
|
const loadingFiles = import.meta.glob('./app/**/*loading.tsx');
|
||||||
|
|
||||||
|
const routes = convertPagesToRoute(files, loadingFiles) as RouteObject;
|
||||||
|
addErrorElementToRoutes(errorFiles, routes);
|
||||||
|
add404PageToRoutesChildren(notFoundFiles, routes);
|
||||||
|
|
||||||
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
...routes,
|
||||||
|
loader: middleware,
|
||||||
|
shouldRevalidate: () => true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (!rootElement) throw new Error('Failed to find the root element');
|
||||||
|
|
||||||
|
createRoot(rootElement).render(
|
||||||
|
<StrictMode>
|
||||||
|
<QueryProvider>
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</QueryProvider>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { LoaderFunctionArgs } from 'react-router-dom';
|
||||||
|
|
||||||
|
export const middleware = async ({ request }: LoaderFunctionArgs) => {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
|
||||||
|
const pathname = url.pathname;
|
||||||
|
|
||||||
|
if (pathname) return null;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
"@nx/react/typings/cssmodule.d.ts",
|
||||||
|
"@nx/react/typings/image.d.ts",
|
||||||
|
"vite/client"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.test.ts",
|
||||||
|
"src/**/*.spec.tsx",
|
||||||
|
"src/**/*.test.tsx",
|
||||||
|
"src/**/*.spec.js",
|
||||||
|
"src/**/*.test.js",
|
||||||
|
"src/**/*.spec.jsx",
|
||||||
|
"src/**/*.test.jsx",
|
||||||
|
"vite.config.ts",
|
||||||
|
"vite.config.mts",
|
||||||
|
"vitest.config.ts",
|
||||||
|
"vitest.config.mts"
|
||||||
|
],
|
||||||
|
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": false,
|
||||||
|
"esModuleInterop": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"types": ["vite/client", "vitest"]
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extends": "../../tsconfig.base.json"
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"types": [
|
||||||
|
"vitest/globals",
|
||||||
|
"vitest/importMeta",
|
||||||
|
"vite/client",
|
||||||
|
"node",
|
||||||
|
"vitest",
|
||||||
|
"@nx/react/typings/cssmodule.d.ts",
|
||||||
|
"@nx/react/typings/image.d.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"vite.config.ts",
|
||||||
|
"vite.config.mts",
|
||||||
|
"vitest.config.ts",
|
||||||
|
"vitest.config.mts",
|
||||||
|
"src/**/*.test.ts",
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.test.tsx",
|
||||||
|
"src/**/*.spec.tsx",
|
||||||
|
"src/**/*.test.js",
|
||||||
|
"src/**/*.spec.js",
|
||||||
|
"src/**/*.test.jsx",
|
||||||
|
"src/**/*.spec.jsx",
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/// <reference types='vitest' />
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||||
|
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
|
||||||
|
|
||||||
|
export default defineConfig(() => ({
|
||||||
|
root: __dirname,
|
||||||
|
cacheDir: '../../node_modules/.vite/apps/backoffice',
|
||||||
|
server: {
|
||||||
|
port: 4200,
|
||||||
|
host: 'localhost',
|
||||||
|
},
|
||||||
|
preview: {
|
||||||
|
port: 4300,
|
||||||
|
host: 'localhost',
|
||||||
|
},
|
||||||
|
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
|
||||||
|
// Uncomment this if you are using workers.
|
||||||
|
// worker: {
|
||||||
|
// plugins: [ nxViteTsPaths() ],
|
||||||
|
// },
|
||||||
|
build: {
|
||||||
|
outDir: '../../dist/apps/backoffice',
|
||||||
|
emptyOutDir: true,
|
||||||
|
reportCompressedSize: true,
|
||||||
|
commonjsOptions: {
|
||||||
|
transformMixedEsModules: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
watch: false,
|
||||||
|
globals: true,
|
||||||
|
environment: 'jsdom',
|
||||||
|
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||||
|
reporters: ['default'],
|
||||||
|
coverage: {
|
||||||
|
reportsDirectory: '../../coverage/apps/backoffice',
|
||||||
|
provider: 'v8' as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
@@ -6,8 +6,8 @@
|
|||||||
<base href="/" />
|
<base href="/" />
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="/logos/simple.svg" />
|
||||||
<link rel="stylesheet" href="/src/styles.css" />
|
<link rel="stylesheet" href="/src/index.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
'@tailwindcss/postcss': {},
|
'@tailwindcss/postcss': {
|
||||||
|
base: join(import.meta.dirname, '../../'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Bai+Jamjuree:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Bai+Jamjuree:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
|
||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
|
@source "../../../libs/ui/**/*.{ts,tsx}";
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--color-primary-50: #f0f8ff;
|
--color-primary-50: #f0f8ff;
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ export default defineConfig(() => ({
|
|||||||
root: __dirname,
|
root: __dirname,
|
||||||
cacheDir: '../../node_modules/.vite/apps/dimentorin',
|
cacheDir: '../../node_modules/.vite/apps/dimentorin',
|
||||||
server: {
|
server: {
|
||||||
port: 4200,
|
port: 3000,
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
port: 4300,
|
port: 3001,
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
},
|
},
|
||||||
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
|
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<base href="/" />
|
<base href="/" />
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="/logos/simple.svg" />
|
||||||
<link rel="stylesheet" href="/src/styles.css" />
|
<link rel="stylesheet" href="/src/index.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
'@tailwindcss/postcss': {},
|
'@tailwindcss/postcss': {
|
||||||
|
base: join(import.meta.dirname, '../../'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 6.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 220 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="47" height="62" viewBox="0 0 47 62" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.5 38.4999L9.19156 38.4166L8.5 38.4999ZM23.5 42.4999L24.0479 42.9301L23.5 42.4999ZM1.45141 0.804946C1.06764 0.831723 0.778242 1.16453 0.805019 1.5483L1.24135 7.80212C1.26813 8.18588 1.60094 8.47528 1.9847 8.4485C2.36847 8.42173 2.65787 8.08892 2.63109 7.70515L2.24324 2.1462L7.80219 1.75835C8.18595 1.73157 8.47535 1.39876 8.44858 1.015C8.4218 0.631233 8.08899 0.341836 7.70523 0.368611L1.45141 0.804946ZM44.8219 58.1179C44.6435 57.777 44.2226 57.6453 43.8818 57.8237C43.5409 58.002 43.40Line truncated
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="79" height="88" viewBox="0 0 79 88" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1.00004 87.8788C0.5147 87.8788 0.121257 87.4853 0.121261 87C0.121265 86.5147 0.514715 86.1212 1.00006 86.1212L1.00004 87.8788ZM43.4999 69.5L44.1213 70.1214L43.4999 69.5ZM74.1915 0.677276C74.6459 0.506859 75.1525 0.737104 75.3229 1.19154L78.1 8.59705C78.2704 9.05149 78.0401 9.55803 77.5857 9.72845C77.1313 9.89887 76.6247 9.66862 76.4543 9.21418L73.9858 2.6315L67.4031 5.10001C66.9487 5.27042 66.4421 5.04018 66.2717 4.58574C66.1013 4.1313 66.3316 3.62475 66.786 3.45434L74.1915 0.677276ZMLine truncated
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,9 @@
|
|||||||
|
<svg width="317" height="170" viewBox="0 0 317 170" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<rect width="317" height="169.613" fill="url(#pattern0_164_317)"/>
|
||||||
|
<defs>
|
||||||
|
<pattern id="pattern0_164_317" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||||
|
<use xlink:href="#image0_164_317" transform="scale(0.00184502 0.00344828)"/>
|
||||||
|
</pattern>
|
||||||
|
<image id="image0_164_317" width="542" height="290" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAh4AAAEiCAYAAAC2k9sBAAAABGdBTUEAALGPC/xhBQAACklpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAAEiJnVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKLine truncated
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 351 KiB |
@@ -0,0 +1,9 @@
|
|||||||
|
<svg width="138" height="52" viewBox="0 0 138 52" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<rect width="137.505" height="51.7538" fill="url(#pattern0_164_316)"/>
|
||||||
|
<defs>
|
||||||
|
<pattern id="pattern0_164_316" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||||
|
<use xlink:href="#image0_164_316" transform="matrix(0.000440723 0 0 0.00117096 -0.173204 -1.3185)"/>
|
||||||
|
</pattern>
|
||||||
|
<image id="image0_164_316" width="3000" height="3000" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAC7gAAAu4CAYAAABxFfV5AAAACXBIWXMAAC4jAAAuIwF4pT92AAX6AElEQVR4nOzaMQEAIAzAsIF/z0MGPRIFFdCzuwMAAAAAAAAAAAAAAL/d3wEAAAAAAAAAAAAAADBjcAcAAAAAAAAAAAAAIMLgDgAAAAAAAAAAAABAgsEdAAAAAAAAAAAAAIAEgzsAAAAAAAAAAAAAAAkGdwAAAAAAAAAAAAAAEgzuAAAAAAAAAAAAAAAkGNwBAAAAAAAAAAAAAEgwuAMAAAAAAAAAAAAAkGBwBwAAAAAAAAAAAAAgweAOAAAAAAAAAAAAAECCwR0AAAAAAAAAAAAAgASDOwAAAAAAAAAAAAAACQZ3AAAALine truncated
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 511 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="283" height="222" viewBox="0 0 283 222" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M41.2124 71.9365L30.3686 94.0759L1 115.312L8.2292 133.385L15.0066 149.65L26.3022 157.783L30.3686 179.471L35.7905 221.491L73.7438 218.328L104.92 214.713L124.8 221.491L181.278 218.328L192.574 210.195L205.225 214.713C213.207 214.864 229.894 215.075 232.786 214.713C235.678 214.352 241.22 219.081 243.63 221.491H263.962L276.613 196.64L281.583 166.368L272.999 154.169L263.962 143.325L254.926 133.385L235.949 127.963V121.185C236.702 116.065 238.298 105.1 238.66 102.209C239.021 99.3171 245.437 90Line truncated
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1017 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="304" height="200" viewBox="0 0 304 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M21.5132 38.0564L0.729248 148.302V175.411C13.531 178.725 40.038 185.351 43.6526 185.351C47.2672 185.351 66.8463 187.761 76.184 188.966L92.9015 167.278H112.33H134.469C137.632 167.278 151.639 173.152 160.675 175.411C169.712 177.67 181.911 183.544 188.688 188.966C195.466 194.388 199.984 193.032 205.858 197.551C210.557 201.165 224.985 199.057 231.612 197.551H258.721L284.023 188.966C288.542 183.243 297.578 170.893 297.578 167.278V139.717V105.378L303 64.7141L291.704 38.0564L278.602 32.6345L2Line truncated
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 941 B |
@@ -4,7 +4,7 @@ import { Navbar } from '@imphnen-frontend-service/ui/organisms';
|
|||||||
|
|
||||||
export const AppLayout: FC = (): ReactElement => {
|
export const AppLayout: FC = (): ReactElement => {
|
||||||
return (
|
return (
|
||||||
<main className="bg-primary-50 min-h-screen px-20 py-[60px]">
|
<main className="bg-primary-50 min-h-screen">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,7 +1,281 @@
|
|||||||
import { FC, ReactElement } from 'react';
|
import { ArrowDownOutlined } from '@ant-design/icons';
|
||||||
|
import { Button } from '@imphnen-frontend-service/ui/atoms';
|
||||||
|
import { InputForm, Modal } from '@imphnen-frontend-service/ui/molecules';
|
||||||
|
import { cn } from '@imphnen-frontend-service/utils';
|
||||||
|
import { FC, Fragment, ReactElement, useEffect, useState } from 'react';
|
||||||
|
import { Link } from 'react-router';
|
||||||
|
|
||||||
|
interface GachaItemProps {
|
||||||
|
src: string;
|
||||||
|
label: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const Components: FC = (): ReactElement => {
|
export const Components: FC = (): ReactElement => {
|
||||||
return <>Hallo</>;
|
// Pinjem
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
|
const scrollToRoulette = () => {
|
||||||
|
const rouletteSection = document.getElementById('roulette');
|
||||||
|
if (rouletteSection) {
|
||||||
|
rouletteSection.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const gachaPlaySection = document.getElementById('gacha-play');
|
||||||
|
if (gachaPlaySection) {
|
||||||
|
let currentIndex = 0;
|
||||||
|
const items = gachaPlaySection.children;
|
||||||
|
const totalItems = items.length;
|
||||||
|
|
||||||
|
const scrollItems = () => {
|
||||||
|
if (currentIndex >= totalItems) {
|
||||||
|
currentIndex = 0;
|
||||||
|
}
|
||||||
|
items[currentIndex].scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
inline: 'center',
|
||||||
|
});
|
||||||
|
currentIndex++;
|
||||||
|
};
|
||||||
|
|
||||||
|
const intervalId = setInterval(scrollItems, 2800);
|
||||||
|
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const GachaItem: FC<GachaItemProps> = ({
|
||||||
|
src,
|
||||||
|
label,
|
||||||
|
className,
|
||||||
|
}): ReactElement => {
|
||||||
|
return (
|
||||||
|
<div className="snap-center flex-auto justify-center items-center flex flex-col min-w-full">
|
||||||
|
<div className="max-h-[180px] md:max-h-[270px] md:max-w-[500px] mb-2 md:mb-5 flex flex-1 justify-center items-center">
|
||||||
|
<img
|
||||||
|
src={src}
|
||||||
|
alt=""
|
||||||
|
className={cn('h-[150px] md:h-[255px] object-contain', className)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="font-semibold text-center md:text-p2">{label}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{/* Landing Page */}
|
||||||
|
<section
|
||||||
|
id="landing-page"
|
||||||
|
className="my-12 md:my-24 grid grid-cols-4 md:grid-cols-8 lg:grid-cols-12 justify-items-center max-w-[1280px] items-center justify-center mx-[32px] md:mx-[60px] lg:mx-[80px] xl:mx-auto"
|
||||||
|
>
|
||||||
|
<div className="order-none lg:order-1 flex flex-col col-span-4 md:col-span-8 lg:col-span-4 md:w-full lg:w-fit items-center gap-7 md:gap-13 text-primary-500 mb-10 md:mb-20">
|
||||||
|
<img
|
||||||
|
src="/logos/logo.svg"
|
||||||
|
alt="IMPHNEN Logo"
|
||||||
|
className="w-[170px] md:w-[317px]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="font-semibold text-center">
|
||||||
|
<div className="text-p1 md:text-h2 lg:font-bold text-center">
|
||||||
|
<p>New Merchandise</p>
|
||||||
|
<p>Coming UP</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-base md:text-p2 mt-3">
|
||||||
|
Periode Gacha: 1 - 31 Maret 2025
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center font-semibold text-p3 md:text-p1">
|
||||||
|
<p>Let's Go Checkout Our Merch &</p>
|
||||||
|
<p>Gacha Your Prize Here</p>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="bordered"
|
||||||
|
className="flex gap-3 px-3 md:px-5 py-2 md:py-4 bg-primary-100 border border-primary-500 border-dashed rounded md:rounded-md animate-bounce text-label2 md:text-p2"
|
||||||
|
onClick={scrollToRoulette}
|
||||||
|
>
|
||||||
|
<ArrowDownOutlined />
|
||||||
|
<span>Scroll For Gacha</span>
|
||||||
|
<ArrowDownOutlined />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="order-1 lg:order-none flex flex-col col-span-4 justify-center items-center mb-8 relative">
|
||||||
|
<div className="relative h-[150px] md:h-[230px]">
|
||||||
|
<img
|
||||||
|
src="/merch/1.png"
|
||||||
|
alt="Merch 1"
|
||||||
|
className="relative top-[3px] md:top-[6px] left-[4px] md:left-[7px] z-10 w-[165px] md:w-[266px]"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
className="absolute top-0 left-0 min-w-[174px] md:min-w-[280px] z-0"
|
||||||
|
src="/merch/Vector-1.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
className="absolute hidden lg:block -bottom-[50px] -right-[50px]"
|
||||||
|
src="/landing-arrow-1.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 py-1 text-primary-500 font-semibold text-base md:text-p2 bg-white shadow-md rounded">
|
||||||
|
Official Merch IMPHNEN
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-primary-500 font-bold text-p2 md:text-h3">
|
||||||
|
~ 175k ~
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="order-2 flex flex-col col-span-4 justify-center items-center mb-8 relative">
|
||||||
|
<div className="relative h-[130px] md:h-[230px]">
|
||||||
|
<img
|
||||||
|
src="/merch/2.png"
|
||||||
|
alt="Merch 2"
|
||||||
|
className="relative top-[5px] md:top-[8px] left-0 md:left-[5px] z-10 w-[165px] md:w-[277px]"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
className="absolute top-0 -left-[5px] md:-left-[8px] z-0 min-w-[174px] md:min-w-[302px]"
|
||||||
|
src="/merch/Vector-2.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
className="absolute hidden lg:block -bottom-[100px] -left-[55px]"
|
||||||
|
src="/landing-arrow-2.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="px-3 py-1 text-primary-500 font-semibold text-base md:text-p2 bg-white shadow-md rounded">
|
||||||
|
IMPHNEN Mini Merch
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-primary-500 font-bold text-p2 md:text-h3">
|
||||||
|
~ 90k ~
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{/* Roulette Page */}
|
||||||
|
<section
|
||||||
|
id="roulette"
|
||||||
|
className="mt-20 pb-50 lg:py-90 mx-[32px] md:mx-[60px] lg:mx-[80px] xl:mx-auto lg:max-w-[1280px] grid grid-cols-4 md:grid-cols-8 lg:grid-cols-12 justify-items-center gap-y-16 md:gap-y-28"
|
||||||
|
>
|
||||||
|
<div className="col-span-4 md:col-span-8 lg:col-span-6 text-primary-500 bg-white rounded-lg md:rounded-xl p-5 md:py-9 md:px-13 shadow md:max-w-[430px]">
|
||||||
|
<header className="mb-4">
|
||||||
|
<h3 className="font-semibold text-p3 md:text-h2">Gacha Roulette</h3>
|
||||||
|
</header>
|
||||||
|
<h4 className="font-semibold my-2 text-base md:text-p1">
|
||||||
|
How to participate
|
||||||
|
</h4>
|
||||||
|
<p className="text-base md:text-p2">
|
||||||
|
Kamu harus melakukan pembelian merch batch 2 di shopee IMPHNEN
|
||||||
|
</p>
|
||||||
|
<h4 className="font-semibold my-2 text-base md:text-p1">
|
||||||
|
How to gacha
|
||||||
|
</h4>
|
||||||
|
<ol className="text-base md:text-p2 list-decimal list-outside ml-4 md:ml-6">
|
||||||
|
<li>First, press the "Spin Now" button</li>
|
||||||
|
<li>
|
||||||
|
If the merch does not match you can reroll by paying IDR5,000
|
||||||
|
</li>
|
||||||
|
<li>If it is appropriate you can submit</li>
|
||||||
|
<li>Then fill in the data for your merch delivery process</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="roulette-spin"
|
||||||
|
className="col-span-4 md:col-span-8 lg:col-span-6 flex flex-col items-center gap-4 md:gap-8 overflow-x-hidden"
|
||||||
|
>
|
||||||
|
<div className="bg-white text-primary-500 font-medium text-p3 md:text-h3 shadow py-2 px-4 md:py-4 md:px-8 max-w-fit rounded-md md:rounded-lg">
|
||||||
|
Here Take Your Prize
|
||||||
|
</div>
|
||||||
|
<section
|
||||||
|
id="gacha-play"
|
||||||
|
className="flex flex-nowrap overflow-auto w-full gap-x-8 snap-x snap-mandatory"
|
||||||
|
>
|
||||||
|
<GachaItem
|
||||||
|
src="/gacha/certificate.png"
|
||||||
|
label="Sertifikat + Laminating"
|
||||||
|
/>
|
||||||
|
<GachaItem
|
||||||
|
src="/gacha/lanyard-id-card.png"
|
||||||
|
label="Lanyard + ID Card"
|
||||||
|
/>
|
||||||
|
<GachaItem src="/gacha/pin.png" label="Pin" />
|
||||||
|
<GachaItem src="/gacha/sticker.png" label="Sticker Isi 3" />
|
||||||
|
<GachaItem src="/gacha/sticker.png" label="Sticker Isi 5" />
|
||||||
|
<GachaItem
|
||||||
|
src="/gacha/gelang-karet.png"
|
||||||
|
label="Gelang Karet"
|
||||||
|
className="h-[86px] md:h-[160px]"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
<Button variant="secondary" size="md">
|
||||||
|
Spin Now
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{/* Diffuser & Cloud */}
|
||||||
|
<div className="sticky bottom-0 h-[86px] md:h-[200px] bg-gradient-to-b from-primary-500/0 to-primary-500/50 to-80%"></div>
|
||||||
|
|
||||||
|
{/**Izin pake untuk debug modals gacha */}
|
||||||
|
<button onClick={() => setShowModal(true)}>Open Modal</button>
|
||||||
|
<Modal
|
||||||
|
className="py-[45px] min-w-[400px] lg:min-w-[455px] px-7"
|
||||||
|
isOpen={showModal}
|
||||||
|
onClose={() => setShowModal(false)}
|
||||||
|
>
|
||||||
|
<Modal.Header className="space-y-4">
|
||||||
|
<img src="/logos/logo.svg" alt="" className="h-[70px] w-auto" />
|
||||||
|
<h1 className="text-primary-500 text-p1 text-center font-semibold">
|
||||||
|
Login
|
||||||
|
</h1>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Content className="space-y-4">
|
||||||
|
<InputForm
|
||||||
|
label="Email"
|
||||||
|
placeholder="Masukkan Email"
|
||||||
|
type="email"
|
||||||
|
size="lg"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
<InputForm
|
||||||
|
label="Password"
|
||||||
|
placeholder="Masukkan password"
|
||||||
|
type="password"
|
||||||
|
size="lg"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
<Link to="/forgot-password">
|
||||||
|
<h1 className="text-end text-primary-500 text-xl font-medium">
|
||||||
|
Lupa Password?
|
||||||
|
</h1>
|
||||||
|
</Link>
|
||||||
|
</Modal.Content>
|
||||||
|
<Modal.Footer className="flex flex-col md:flex-col pt-4">
|
||||||
|
<Button
|
||||||
|
size="md"
|
||||||
|
className="w-full"
|
||||||
|
onClick={() => console.log('Login')}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
<div className="flex justify-center gap-2 pt-2.5 font-medium">
|
||||||
|
<p className="text-neutral-500">Belum punya akun?</p>
|
||||||
|
<Link
|
||||||
|
className="text-primary-500 hover:text-primary-600"
|
||||||
|
to="/register"
|
||||||
|
>
|
||||||
|
Daftar disini
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal>
|
||||||
|
{/**Izin pake untuk debug modals gacha */}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Components;
|
export default Components;
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Bai+Jamjuree:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Bai+Jamjuree:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
|
||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
|
@source "../../../libs/ui/**/*.{ts,tsx}";
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--color-primary-50: #f0f8ff;
|
--color-primary-50: #f0f8ff;
|
||||||
--color-primary-100: #e1f0fd;
|
--color-primary-100: #e1f0fd;
|
||||||
--color-primary-200: #b8e1f8;
|
--color-primary-200: #bce1fb;
|
||||||
--color-primary-300: #a1d4f0;
|
--color-primary-300: #81cbf8;
|
||||||
--color-primary-400: #4aa4da;
|
--color-primary-400: #3eb0f2;
|
||||||
--color-primary-500: #1a8ce6;
|
--color-primary-500: #23a1eb;
|
||||||
--color-primary-600: #1673c7;
|
--color-primary-600: #0877c1;
|
||||||
--color-primary-700: #0f5ca5;
|
--color-primary-700: #085f9c;
|
||||||
--color-primary-800: #0a4780;
|
--color-primary-800: #0b5181;
|
||||||
--color-primary-900: #04305d;
|
--color-primary-900: #0f446b;
|
||||||
|
--color-primary-950: #0a2b47;
|
||||||
|
|
||||||
--color-neutral-50: #f7f7f7;
|
--color-neutral-50: #f6f6f6;
|
||||||
--color-neutral-100: #e7e7e7;
|
--color-neutral-100: #e7e7e7;
|
||||||
--color-neutral-200: #d0d0d0;
|
--color-neutral-200: #d0d0d0;
|
||||||
--color-neutral-300: #b7b7b7;
|
--color-neutral-300: #b7b7b7;
|
||||||
@@ -23,50 +25,47 @@
|
|||||||
--color-neutral-700: #4a4a4a;
|
--color-neutral-700: #4a4a4a;
|
||||||
--color-neutral-800: #3a3a3a;
|
--color-neutral-800: #3a3a3a;
|
||||||
--color-neutral-900: #2f2f2f;
|
--color-neutral-900: #2f2f2f;
|
||||||
|
--color-neutral-950: #2b2b2b;
|
||||||
|
|
||||||
--color-success-50: #e7f2ee;
|
--color-success-100: #e0fbd8;
|
||||||
--color-success-100: #cde6dd;
|
--color-success-200: #bcf8b0;
|
||||||
--color-success-200: #9bccbc;
|
--color-success-300: #8eea85;
|
||||||
--color-success-300: #78bdab;
|
--color-success-400: #63d564;
|
||||||
--color-success-400: #4ca88f;
|
--color-success-500: #35ba43;
|
||||||
--color-success-500: #349078;
|
--color-success-600: #269f3e;
|
||||||
--color-success-600: #2f7c66;
|
--color-success-700: #1a8439;
|
||||||
--color-success-700: #26624f;
|
--color-success-800: #106b32;
|
||||||
--color-success-800: #1e4a3b;
|
--color-success-900: #0b592f;
|
||||||
--color-success-900: #1b513b;
|
|
||||||
|
|
||||||
--color-info-50: #e7f4f5;
|
--color-info-100: #ccfcfe;
|
||||||
--color-info-100: #cce9ea;
|
--color-info-200: #9bf3fd;
|
||||||
--color-info-200: #99d3d5;
|
--color-info-300: #67e3fb;
|
||||||
--color-info-300: #78c4c7;
|
--color-info-400: #42cdf8;
|
||||||
--color-info-400: #3aa9ad;
|
--color-info-500: #04acf3;
|
||||||
--color-info-500: #279599;
|
--color-info-600: #0185d0;
|
||||||
--color-info-600: #207d82;
|
--color-info-700: #0264af;
|
||||||
--color-info-700: #1a666b;
|
--color-info-800: #01478d;
|
||||||
--color-info-800: #124c52;
|
--color-info-900: #003375;
|
||||||
--color-info-900: #093d43;
|
|
||||||
|
|
||||||
--color-warning-50: #fffce6;
|
--color-warning-100: #fffcd3;
|
||||||
--color-warning-100: #fff8cc;
|
--color-warning-200: #fffaa9;
|
||||||
--color-warning-200: #fef39b;
|
--color-warning-300: #fff67d;
|
||||||
--color-warning-300: #fde967;
|
--color-warning-400: #fff25d;
|
||||||
--color-warning-400: #fbd93d;
|
--color-warning-500: #ffed27;
|
||||||
--color-warning-500: #f3c215;
|
--color-warning-600: #dbc91d;
|
||||||
--color-warning-600: #d7a20f;
|
--color-warning-700: #b7a714;
|
||||||
--color-warning-700: #aa7e0c;
|
--color-warning-800: #93850b;
|
||||||
--color-warning-800: #805c07;
|
--color-warning-900: #7a6d07;
|
||||||
--color-warning-900: #665c00;
|
|
||||||
|
|
||||||
--color-danger-50: #ffe7e7;
|
--color-danger-100: #ffe8da;
|
||||||
--color-danger-100: #ffcece;
|
--color-danger-200: #ffcbb3;
|
||||||
--color-danger-200: #ff9f9f;
|
--color-danger-300: #ffaa8d;
|
||||||
--color-danger-300: #ff6e6e;
|
--color-danger-400: #ff8870;
|
||||||
--color-danger-400: #fa4646;
|
--color-danger-500: #ff5242;
|
||||||
--color-danger-500: #ef1f1f;
|
--color-danger-600: #da3030;
|
||||||
--color-danger-600: #d11515;
|
--color-danger-700: #b7212d;
|
||||||
--color-danger-700: #a51111;
|
--color-danger-800: #93152a;
|
||||||
--color-danger-800: #7f0c0c;
|
--color-danger-900: #7a0c27;
|
||||||
--color-danger-900: #5d2d2d;
|
|
||||||
|
|
||||||
--radius-none: 0px;
|
--radius-none: 0px;
|
||||||
--radius-sm: 2px;
|
--radius-sm: 2px;
|
||||||
@@ -79,16 +78,28 @@
|
|||||||
|
|
||||||
--font-bai-jamjuree: 'Bai Jamjuree', sans-serif;
|
--font-bai-jamjuree: 'Bai Jamjuree', sans-serif;
|
||||||
|
|
||||||
--text-h1: 3rem; /* ~48px */
|
--text-h1: 3.833rem;
|
||||||
--text-h2: 2.4rem; /* ~38.4px */
|
/* ~46px */
|
||||||
--text-h3: 1.92rem; /* ~30.72px */
|
--text-h2: 3.083rem;
|
||||||
--text-p1: 1.54rem; /* ~24.58px */
|
/* ~37px */
|
||||||
--text-p2: 1.23rem; /* ~19.68px */
|
--text-h3: 2.417rem;
|
||||||
--text-label1: 0.98rem; /* ~15.74px */
|
/* ~29px */
|
||||||
--text-label2: 0.78rem; /* ~12.59px */
|
--text-p1: 1.917rem;
|
||||||
|
/* ~23px */
|
||||||
|
--text-p2: 1.583rem;
|
||||||
|
/* ~19px */
|
||||||
|
--text-p3: 1.25rem;
|
||||||
|
/* 15px */
|
||||||
|
--text-label1: 1rem;
|
||||||
|
/* 12px */
|
||||||
|
--text-label2: 0.833rem;
|
||||||
|
/* ~10px */
|
||||||
|
--text-label3: 0.677rem;
|
||||||
|
/* ~8px */
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
|
||||||
*,
|
*,
|
||||||
::after,
|
::after,
|
||||||
::before,
|
::before,
|
||||||
@@ -102,13 +113,16 @@
|
|||||||
width: 6px;
|
width: 6px;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: var(--color-neutral-50);
|
background: var(--color-neutral-50);
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: #cccccc;
|
background: #cccccc;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: var(--color-neutral-300);
|
background: var(--color-neutral-300);
|
||||||
}
|
}
|
||||||
@@ -116,5 +130,7 @@
|
|||||||
html {
|
html {
|
||||||
font-family: 'Bai Jamjuree', sans-serif;
|
font-family: 'Bai Jamjuree', sans-serif;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
|
||||||
@@ -8,11 +8,11 @@ export default defineConfig(() => ({
|
|||||||
root: __dirname,
|
root: __dirname,
|
||||||
cacheDir: '../../node_modules/.vite/apps/gacha',
|
cacheDir: '../../node_modules/.vite/apps/gacha',
|
||||||
server: {
|
server: {
|
||||||
port: 4200,
|
port: 3000,
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
port: 4300,
|
port: 3001,
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
},
|
},
|
||||||
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
|
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
|
||||||
|
|||||||