chore: initial commit

This commit is contained in:
Maulana Sodiqin
2025-03-13 02:03:13 +07:00
parent d86d77c154
commit 4ec41164f8
67 changed files with 6111 additions and 1259 deletions
+28
View File
@@ -0,0 +1,28 @@
# path to a directory with all packages
storage: ../tmp/local-registry/storage
# a list of other known repositories we can talk to
uplinks:
npmjs:
url: https://registry.npmjs.org/
maxage: 60m
packages:
'**':
# give all users (including non-authenticated users) full access
# because it is a local registry
access: $all
publish: $all
unpublish: $all
# if package is not available locally, proxy requests to npm registry
proxy: npmjs
# log settings
log:
type: stdout
format: pretty
level: warn
publish:
allow_offline: true # set offline to true to allow publish offline
+5
View File
@@ -0,0 +1,5 @@
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
-1
View File
@@ -1 +0,0 @@
/* Your styles goes here. */
-17
View File
@@ -1,17 +0,0 @@
import { render } from '@testing-library/react';
import App from './app';
describe('App', () => {
it('should render successfully', () => {
const { baseElement } = render(<App />);
expect(baseElement).toBeTruthy();
});
it('should have a greeting as the title', () => {
const { getAllByText } = render(<App />);
expect(
getAllByText(new RegExp('Welcome dimentorin', 'gi')).length > 0
).toBeTruthy();
});
});
-13
View File
@@ -1,13 +0,0 @@
// Uncomment this line to use CSS modules
// import styles from './app.module.css';
import NxWelcome from './nx-welcome';
export function App() {
return (
<div>
<NxWelcome title="dimentorin" />
</div>
);
}
export default App;
File diff suppressed because one or more lines are too long
+7
View File
@@ -0,0 +1,7 @@
import { FC, ReactElement } from 'react';
export const Components: FC = (): ReactElement => {
return <>Hallo</>;
};
export default Components;
+120
View File
@@ -0,0 +1,120 @@
@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';
@theme {
--color-primary-50: #f0f8ff;
--color-primary-100: #e1f0fd;
--color-primary-200: #b8e1f8;
--color-primary-300: #a1d4f0;
--color-primary-400: #4aa4da;
--color-primary-500: #1a8ce6;
--color-primary-600: #1673c7;
--color-primary-700: #0f5ca5;
--color-primary-800: #0a4780;
--color-primary-900: #04305d;
--color-neutral-50: #f7f7f7;
--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-success-50: #e7f2ee;
--color-success-100: #cde6dd;
--color-success-200: #9bccbc;
--color-success-300: #78bdab;
--color-success-400: #4ca88f;
--color-success-500: #349078;
--color-success-600: #2f7c66;
--color-success-700: #26624f;
--color-success-800: #1e4a3b;
--color-success-900: #1b513b;
--color-info-50: #e7f4f5;
--color-info-100: #cce9ea;
--color-info-200: #99d3d5;
--color-info-300: #78c4c7;
--color-info-400: #3aa9ad;
--color-info-500: #279599;
--color-info-600: #207d82;
--color-info-700: #1a666b;
--color-info-800: #124c52;
--color-info-900: #093d43;
--color-warning-50: #fffce6;
--color-warning-100: #fff8cc;
--color-warning-200: #fef39b;
--color-warning-300: #fde967;
--color-warning-400: #fbd93d;
--color-warning-500: #f3c215;
--color-warning-600: #d7a20f;
--color-warning-700: #aa7e0c;
--color-warning-800: #805c07;
--color-warning-900: #665c00;
--color-danger-50: #ffe7e7;
--color-danger-100: #ffcece;
--color-danger-200: #ff9f9f;
--color-danger-300: #ff6e6e;
--color-danger-400: #fa4646;
--color-danger-500: #ef1f1f;
--color-danger-600: #d11515;
--color-danger-700: #a51111;
--color-danger-800: #7f0c0c;
--color-danger-900: #5d2d2d;
--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: 3rem; /* ~48px */
--text-h2: 2.4rem; /* ~38.4px */
--text-h3: 1.92rem; /* ~30.72px */
--text-p1: 1.54rem; /* ~24.58px */
--text-p2: 1.23rem; /* ~19.68px */
--text-label1: 0.98rem; /* ~15.74px */
--text-label2: 0.78rem; /* ~12.59px */
}
@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;
}
}
+34 -7
View File
@@ -1,13 +1,40 @@
import { createRoot } from 'react-dom/client';
import { middleware } from './middleware';
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom/client';
import App from './app/app';
import { createBrowserRouter, RouteObject, RouterProvider } from 'react-router';
import {
add404PageToRoutesChildren,
addErrorElementToRoutes,
convertPagesToRoute,
QueryProvider,
} from '@imphnen-frontend-service/utils';
import './index.css';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
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');
root.render(
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>
<App />
<QueryProvider>
<RouterProvider router={router} />
</QueryProvider>
</StrictMode>
);
+11
View File
@@ -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;
};
+12
View File
@@ -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: {},
},
];
+16
View File
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Gacha</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>
+5
View File
@@ -0,0 +1,5 @@
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
+9
View File
@@ -0,0 +1,9 @@
{
"name": "gacha",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/gacha/src",
"projectType": "application",
"tags": [],
"// targets": "to see all targets run: nx show project gacha --web",
"targets": {}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+14
View File
@@ -0,0 +1,14 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
import { Navbar } from '@imphnen-frontend-service/ui/organisms';
export const AppLayout: FC = (): ReactElement => {
return (
<main className="bg-primary-50 min-h-screen px-20 py-[60px]">
<Navbar />
<Outlet />
</main>
);
};
export default AppLayout;
+7
View File
@@ -0,0 +1,7 @@
import { FC, ReactElement } from 'react';
export const Components: FC = (): ReactElement => {
return <>Hallo</>;
};
export default Components;
+120
View File
@@ -0,0 +1,120 @@
@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';
@theme {
--color-primary-50: #f0f8ff;
--color-primary-100: #e1f0fd;
--color-primary-200: #b8e1f8;
--color-primary-300: #a1d4f0;
--color-primary-400: #4aa4da;
--color-primary-500: #1a8ce6;
--color-primary-600: #1673c7;
--color-primary-700: #0f5ca5;
--color-primary-800: #0a4780;
--color-primary-900: #04305d;
--color-neutral-50: #f7f7f7;
--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-success-50: #e7f2ee;
--color-success-100: #cde6dd;
--color-success-200: #9bccbc;
--color-success-300: #78bdab;
--color-success-400: #4ca88f;
--color-success-500: #349078;
--color-success-600: #2f7c66;
--color-success-700: #26624f;
--color-success-800: #1e4a3b;
--color-success-900: #1b513b;
--color-info-50: #e7f4f5;
--color-info-100: #cce9ea;
--color-info-200: #99d3d5;
--color-info-300: #78c4c7;
--color-info-400: #3aa9ad;
--color-info-500: #279599;
--color-info-600: #207d82;
--color-info-700: #1a666b;
--color-info-800: #124c52;
--color-info-900: #093d43;
--color-warning-50: #fffce6;
--color-warning-100: #fff8cc;
--color-warning-200: #fef39b;
--color-warning-300: #fde967;
--color-warning-400: #fbd93d;
--color-warning-500: #f3c215;
--color-warning-600: #d7a20f;
--color-warning-700: #aa7e0c;
--color-warning-800: #805c07;
--color-warning-900: #665c00;
--color-danger-50: #ffe7e7;
--color-danger-100: #ffcece;
--color-danger-200: #ff9f9f;
--color-danger-300: #ff6e6e;
--color-danger-400: #fa4646;
--color-danger-500: #ef1f1f;
--color-danger-600: #d11515;
--color-danger-700: #a51111;
--color-danger-800: #7f0c0c;
--color-danger-900: #5d2d2d;
--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: 3rem; /* ~48px */
--text-h2: 2.4rem; /* ~38.4px */
--text-h3: 1.92rem; /* ~30.72px */
--text-p1: 1.54rem; /* ~24.58px */
--text-p2: 1.23rem; /* ~19.68px */
--text-label1: 0.98rem; /* ~15.74px */
--text-label2: 0.78rem; /* ~12.59px */
}
@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;
}
}
+40
View File
@@ -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>
);
+11
View File
@@ -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;
};
+27
View File
@@ -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"]
}
+21
View File
@@ -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"
}
+30
View File
@@ -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"
]
}
+42
View File
@@ -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/gacha',
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/gacha',
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/gacha',
provider: 'v8' as const,
},
},
}));
+12
View File
@@ -0,0 +1,12 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
+7
View File
@@ -0,0 +1,7 @@
# ui
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test ui` to execute the unit tests via [Vitest](https://vitest.dev/).
+12
View File
@@ -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: {},
},
];
+12
View File
@@ -0,0 +1,12 @@
{
"name": "@imphnen-frontend-service/ui",
"version": "0.0.1",
"main": "./index.js",
"types": "./index.d.ts",
"exports": {
".": {
"import": "./index.mjs",
"require": "./index.js"
}
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"name": "ui",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/ui/src",
"projectType": "library",
"tags": [],
"targets": {
"nx-release-publish": {
"options": {
"packageRoot": "dist/{projectRoot}"
}
}
},
"release": {
"version": {
"generatorOptions": {
"packageRoot": "dist/{projectRoot}",
"currentVersionResolver": "git-tag",
"fallbackCurrentVersionResolver": "disk"
}
}
}
}
+48
View File
@@ -0,0 +1,48 @@
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './button';
describe('Test Button Component', () => {
it('renders the button with children text', () => {
render(<Button>Click Me</Button>);
expect(screen.getByText('Click Me')).toBeInTheDocument();
});
it('calls onClick when clicked', async () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click Me</Button>);
const user = userEvent.setup();
await user.click(screen.getByText('Click Me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('applies the correct variant class', () => {
render(<Button variant="danger">Delete</Button>);
const button = screen.getByText('Delete');
expect(button).toHaveClass('bg-danger-500');
expect(button).toHaveClass('hover:bg-danger-600');
expect(button).toHaveClass('text-white');
});
it("disables the button when 'disabled' prop is set", async () => {
const handleClick = vi.fn();
render(
<Button variant="primary" disabled onClick={handleClick}>
Disabled
</Button>
);
const user = userEvent.setup();
const button = screen.getByText('Disabled');
expect(button).toBeDisabled();
await user.click(button);
expect(handleClick).not.toHaveBeenCalled();
});
});
+67
View File
@@ -0,0 +1,67 @@
import {
FC,
ReactElement,
ButtonHTMLAttributes,
DetailedHTMLProps,
} from 'react';
import { cn } from '@imphnen-frontend-service/utils';
type TButtonVariant =
| 'primary'
| 'secondary'
| 'success'
| 'danger'
| 'text'
| 'bordered';
type TButtonSize = 'sm' | 'md' | 'lg';
type TButtonProps = DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
> & {
variant?: TButtonVariant;
size?: TButtonSize;
};
const variantClasses: Record<TButtonVariant, string> = {
primary: 'bg-primary-500 hover:bg-primary-600 text-white shadow-md',
secondary:
'bg-white hover:text-primary-600 hover:bg-gray-50 text-primary-500 shadow-md',
text: 'bg-transparent hover:text-primary-600 hover:bg-gray-50 text-primary-500',
bordered:
'border border-primary-500 hover:border-primary-600 bg-transparent hover:text-primary-600 hover:bg-gray-50 text-primary-500',
success: 'bg-success-500 hover:bg-success-600 text-white shadow-md',
danger: 'bg-danger-500 hover:bg-danger-600 text-white shadow-md',
};
const sizeClasses: Record<TButtonSize, string> = {
sm: 'text-[12px] max-h-[36px]',
md: 'text-[15px] max-h-[40px]',
lg: 'text-[19px] max-h-[44px]',
};
const disabledClass = 'opacity-50 cursor-not-allowed';
export const Button: FC<TButtonProps> = ({
variant = 'primary',
size = 'md',
disabled,
className,
children,
...rest
}): ReactElement => {
const mergedClassName = cn(
'inline-flex items-center justify-center font-[600] rounded-lg px-[16px] py-[10px]',
'transition-colors duration-200 cursor-pointer',
sizeClasses[size],
variantClasses[variant],
disabled && disabledClass,
className
);
return (
<button className={mergedClassName} disabled={disabled} {...rest}>
{children}
</button>
);
};
+1
View File
@@ -0,0 +1 @@
export * from './button';
+1
View File
@@ -0,0 +1 @@
export * from './button';
+1
View File
@@ -0,0 +1 @@
export * from './navbar';
+1
View File
@@ -0,0 +1 @@
export * from './navbar';
+74
View File
@@ -0,0 +1,74 @@
import { MenuOutlined } from '@ant-design/icons';
import { FC, ReactElement, useState } from 'react';
import { Link } from 'react-router-dom';
export const Navbar: FC = (): ReactElement => {
const [isDropdownOpen, setDropdownOpen] = useState(false);
return (
<header
className="bg-white shadow-sm rounded-lg min-h-[47px] max-h-[47px] md:min-h-[60px] md:max-h-[60px] lg:min-h-[71px] lg:max-h-[71px] flex justify-between w-full max-w-[1280px] xl:mx-auto sticky"
role="navigation"
>
<div className="flex w-full items-center justify-between px-6 py-3">
<div className="flex items-center">
<img
src="/logos/simple.svg"
alt="IMPHNEN Logo"
className="h-8 w-auto"
/>
</div>
<nav className="w-full flex justify-end">
<ul className="items-center gap-x-8 font-semibold hidden md:flex">
<li>
<Link
to="#"
className="text-primary-500 hover:text-primary-600 transition-colors"
>
Home
</Link>
</li>
<li>
<Link to="#" className="text-gray-600 transition-colors">
Merch Gacha
</Link>
</li>
</ul>
<button
className={`md:hidden duration-200 ${
isDropdownOpen ? 'transform rotate-90' : ''
}`}
onClick={() => setDropdownOpen(!isDropdownOpen)}
>
<MenuOutlined style={{ color: '#1a8ce6' }} />
</button>
<div className="relative">
{isDropdownOpen && (
<div className="absolute right-0 top-0 mt-5">
<ul className="mt-2 w-48 bg-white shadow-md border rounded-[16px] border-gray-200 px-5 py-3">
<li>
<Link
to="#"
className="block text-primary-500 hover:text-primary-600 transition-colors px-4 py-2 text-center font-semibold"
>
Home
</Link>
</li>
<li>
<Link
to="#"
className="block text-gray-600 transition-colors px-4 py-2 text-center font-semibold"
>
Merch Gacha
</Link>
</li>
</ul>
</div>
)}
</div>
</nav>
</div>
</header>
);
};
+21
View File
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"types": ["vite/client", "vitest", "@testing-library/jest-dom"]
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"extends": "../../tsconfig.base.json"
}
+35
View File
@@ -0,0 +1,35 @@
{
"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": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"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"
],
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
}
+28
View File
@@ -0,0 +1,28 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
]
},
"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"
]
}
+64
View File
@@ -0,0 +1,64 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
import * as path from 'path';
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/libs/ui',
plugins: [
react(),
nxViteTsPaths(),
nxCopyAssetsPlugin(['*.md']),
dts({
entryRoot: 'src',
tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'),
}),
],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
// Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode
build: {
outDir: '../../dist/libs/ui',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
lib: {
// Could also be a dictionary or array of multiple entry points.
entry: [
'src/atoms/index.ts',
'src/molecules/index.ts',
'src/organisms/index.ts',
],
name: 'ui',
fileName: 'index',
// Change this to the formats you want to support.
// Don't forget to update your package.json as well.
formats: ['es' as const],
},
rollupOptions: {
// External packages that should not be bundled into your library.
external: ['react', 'react-dom', 'react/jsx-runtime'],
},
},
test: {
watch: false,
globals: true,
setupFiles: ['./vitest.setup.ts'],
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/libs/ui',
provider: 'v8' as const,
},
},
}));
+1
View File
@@ -0,0 +1 @@
import '@testing-library/jest-dom';
+12
View File
@@ -0,0 +1,12 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
+7
View File
@@ -0,0 +1,7 @@
# utils
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test utils` to execute the unit tests via [Vitest](https://vitest.dev/).
+12
View File
@@ -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: {},
},
];
+12
View File
@@ -0,0 +1,12 @@
{
"name": "@imphnen-frontend-service/utils",
"version": "0.0.1",
"main": "./index.js",
"types": "./index.d.ts",
"exports": {
".": {
"import": "./index.mjs",
"require": "./index.js"
}
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"name": "utils",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/utils/src",
"projectType": "library",
"tags": [],
"targets": {
"nx-release-publish": {
"options": {
"packageRoot": "dist/{projectRoot}"
}
}
},
"release": {
"version": {
"generatorOptions": {
"packageRoot": "dist/{projectRoot}",
"currentVersionResolver": "git-tag",
"fallbackCurrentVersionResolver": "disk"
}
}
}
}
+7
View File
@@ -0,0 +1,7 @@
import axios, { AxiosRequestConfig } from 'axios';
const config: AxiosRequestConfig = {
baseURL: import.meta.env.VITE_API_URL,
};
export const api = axios.create(config);
+1
View File
@@ -0,0 +1 @@
export * from './api';

Some files were not shown because too many files have changed in this diff Show More