diff --git a/apps/backoffice/eslint.config.mjs b/apps/backoffice/eslint.config.mjs new file mode 100644 index 0000000..e8e4590 --- /dev/null +++ b/apps/backoffice/eslint.config.mjs @@ -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: {}, + }, +]; diff --git a/apps/backoffice/index.html b/apps/backoffice/index.html new file mode 100644 index 0000000..3905403 --- /dev/null +++ b/apps/backoffice/index.html @@ -0,0 +1,16 @@ + + + + + Backoffice + + + + + + + +
+ + + diff --git a/apps/backoffice/postcss.config.mjs b/apps/backoffice/postcss.config.mjs new file mode 100644 index 0000000..63252e5 --- /dev/null +++ b/apps/backoffice/postcss.config.mjs @@ -0,0 +1,9 @@ +import { join } from 'path'; + +export default { + plugins: { + '@tailwindcss/postcss': { + base: join(import.meta.dirname, '../../'), + }, + }, +}; diff --git a/apps/backoffice/project.json b/apps/backoffice/project.json new file mode 100644 index 0000000..6ee852a --- /dev/null +++ b/apps/backoffice/project.json @@ -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": {} +} diff --git a/apps/backoffice/public/favicon.ico b/apps/backoffice/public/favicon.ico new file mode 100644 index 0000000..317ebcb Binary files /dev/null and b/apps/backoffice/public/favicon.ico differ diff --git a/apps/backoffice/src/app/layout.tsx b/apps/backoffice/src/app/layout.tsx new file mode 100644 index 0000000..7428943 --- /dev/null +++ b/apps/backoffice/src/app/layout.tsx @@ -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 ( +
+ + +
+ ); +}; + +export default AppLayout; diff --git a/apps/backoffice/src/app/page.tsx b/apps/backoffice/src/app/page.tsx new file mode 100644 index 0000000..f135208 --- /dev/null +++ b/apps/backoffice/src/app/page.tsx @@ -0,0 +1,7 @@ +import { FC, ReactElement } from 'react'; + +export const Components: FC = (): ReactElement => { + return <>Hallo; +}; + +export default Components; diff --git a/apps/backoffice/src/index.css b/apps/backoffice/src/index.css new file mode 100644 index 0000000..e9bf12a --- /dev/null +++ b/apps/backoffice/src/index.css @@ -0,0 +1,140 @@ +@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-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: 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; + } +} diff --git a/apps/backoffice/src/main.tsx b/apps/backoffice/src/main.tsx new file mode 100644 index 0000000..7201952 --- /dev/null +++ b/apps/backoffice/src/main.tsx @@ -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( + + + + + +); diff --git a/apps/backoffice/src/middleware.ts b/apps/backoffice/src/middleware.ts new file mode 100644 index 0000000..f32b107 --- /dev/null +++ b/apps/backoffice/src/middleware.ts @@ -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; +}; diff --git a/apps/backoffice/tsconfig.app.json b/apps/backoffice/tsconfig.app.json new file mode 100644 index 0000000..6d35def --- /dev/null +++ b/apps/backoffice/tsconfig.app.json @@ -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"] +} diff --git a/apps/backoffice/tsconfig.json b/apps/backoffice/tsconfig.json new file mode 100644 index 0000000..fdfab6c --- /dev/null +++ b/apps/backoffice/tsconfig.json @@ -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" +} diff --git a/apps/backoffice/tsconfig.spec.json b/apps/backoffice/tsconfig.spec.json new file mode 100644 index 0000000..9d3bb72 --- /dev/null +++ b/apps/backoffice/tsconfig.spec.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" + ] +} diff --git a/apps/backoffice/vite.config.ts b/apps/backoffice/vite.config.ts new file mode 100644 index 0000000..936ed86 --- /dev/null +++ b/apps/backoffice/vite.config.ts @@ -0,0 +1,42 @@ +/// +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, + }, + }, +})); diff --git a/apps/gacha/public/gacha/certificate.png b/apps/gacha/public/gacha/certificate.png new file mode 100644 index 0000000..a7a9d23 Binary files /dev/null and b/apps/gacha/public/gacha/certificate.png differ diff --git a/apps/gacha/public/gacha/gelang-karet.png b/apps/gacha/public/gacha/gelang-karet.png new file mode 100644 index 0000000..6e0f466 Binary files /dev/null and b/apps/gacha/public/gacha/gelang-karet.png differ diff --git a/apps/gacha/public/gacha/lanyard-id-card.png b/apps/gacha/public/gacha/lanyard-id-card.png new file mode 100644 index 0000000..082efc7 Binary files /dev/null and b/apps/gacha/public/gacha/lanyard-id-card.png differ diff --git a/apps/gacha/public/gacha/pin.png b/apps/gacha/public/gacha/pin.png new file mode 100644 index 0000000..5eb0c5c Binary files /dev/null and b/apps/gacha/public/gacha/pin.png differ diff --git a/apps/gacha/public/gacha/sticker.png b/apps/gacha/public/gacha/sticker.png new file mode 100644 index 0000000..fffb2a3 Binary files /dev/null and b/apps/gacha/public/gacha/sticker.png differ diff --git a/apps/gacha/src/app/layout.tsx b/apps/gacha/src/app/layout.tsx index 846ecea..7428943 100644 --- a/apps/gacha/src/app/layout.tsx +++ b/apps/gacha/src/app/layout.tsx @@ -4,7 +4,7 @@ import { Navbar } from '@imphnen-frontend-service/ui/organisms'; export const AppLayout: FC = (): ReactElement => { return ( -
+
diff --git a/apps/gacha/src/app/page.tsx b/apps/gacha/src/app/page.tsx index 3296fe1..eefefed 100644 --- a/apps/gacha/src/app/page.tsx +++ b/apps/gacha/src/app/page.tsx @@ -1,16 +1,225 @@ +import { ArrowDownOutlined } from '@ant-design/icons'; import { Button } from '@imphnen-frontend-service/ui/atoms'; -import { LandingPage } from '@imphnen-frontend-service/ui/organisms'; -import { FC, ReactElement, useState } from 'react'; - +import { ModalsGacha } from '@imphnen-frontend-service/ui/organisms'; +import { cn } from '@imphnen-frontend-service/utils'; +import { FC, Fragment, ReactElement, useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; -import { ModalsGacha } from "@imphnen-frontend-service/ui/organisms" + +interface GachaItemProps { + src: string; + label: string; + className?: string; +} export const Components: FC = (): ReactElement => { - const [showModal, setShowModal] = useState(false) + // 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 = ({ + src, + label, + className, + }): ReactElement => { + return ( +
+
+ +
+

{label}

+
+ ); + }; return ( - <> - + + {/* Landing Page */} +
+
+ IMPHNEN Logo + +
+
+

New Merchandise

+

Coming UP

+
+
+ Periode Gacha: 1 - 31 Maret 2025 +
+
+ +
+

Let's Go Checkout Our Merch &

+

Gacha Your Prize Here

+
+ +
+ +
+
+ Merch 1 + + +
+
+ Official Merch IMPHNEN +
+
+ ~ 175k ~ +
+
+
+
+ Merch 2 + + +
+
+ IMPHNEN Mini Merch +
+
+ ~ 90k ~ +
+
+
+ {/* Roulette Page */} +
+
+
+

Gacha Roulette

+
+

+ How to participate +

+

+ Kamu harus melakukan pembelian merch batch 2 di shopee IMPHNEN +

+

+ How to gacha +

+
    +
  1. First, press the "Spin Now" button
  2. +
  3. + If the merch does not match you can reroll by paying IDR5,000 +
  4. +
  5. If it is appropriate you can submit
  6. +
  7. Then fill in the data for your merch delivery process
  8. +
+
+ +
+
+ Here Take Your Prize +
+
+ + + + + + +
+ +
+
+ {/* Diffuser & Cloud */} +
{/**Izin pake untuk debug modals gacha */}
@@ -24,7 +233,7 @@ export const Components: FC = (): ReactElement => { document.body )} {/**Izin pake untuk debug modals gacha */} - + ); }; diff --git a/libs/ui/src/organisms/index.ts b/libs/ui/src/organisms/index.ts index 717fdf1..1665ad1 100644 --- a/libs/ui/src/organisms/index.ts +++ b/libs/ui/src/organisms/index.ts @@ -1,3 +1,2 @@ export * from './navbar'; -export * from './landing-page'; -export * from "./modals-gacha"; \ No newline at end of file +export * from "./modals-gacha"; diff --git a/libs/ui/src/organisms/landing-page/index.ts b/libs/ui/src/organisms/landing-page/index.ts deleted file mode 100644 index d78baa5..0000000 --- a/libs/ui/src/organisms/landing-page/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './landing-page'; diff --git a/libs/ui/src/organisms/landing-page/landing-page.tsx b/libs/ui/src/organisms/landing-page/landing-page.tsx deleted file mode 100644 index 711445d..0000000 --- a/libs/ui/src/organisms/landing-page/landing-page.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { ArrowDownOutlined } from '@ant-design/icons'; -import { Button } from '@imphnen-frontend-service/ui/atoms'; -import { FC, ReactElement } from 'react'; - -export const LandingPage: FC = (): ReactElement => { - const scrollToRoulette = () => { - const rouletteSection = document.getElementById('roulette'); - if (rouletteSection) { - rouletteSection.scrollIntoView({ behavior: 'smooth' }); - } - }; - - return ( -
-
- IMPHNEN Logo - -
-
-

New Merchandise

-

Coming UP

-
-
- Periode Gacha: 1 - 31 Maret 2025 -
-
- -
-

Let's Go Checkout Our Merch &

-

Gacha Your Prize Here

-
- -
- -
-
- Merch 1 - -
-
- Official Merch IMPHNEN -
-
- ~ 175k ~ -
- -
-
-
- Merch 2 - -
-
- IMPHNEN Mini Merch -
-
- ~ 90k ~ -
- -
-
- ); -}; diff --git a/libs/ui/src/organisms/navbar/navbar.tsx b/libs/ui/src/organisms/navbar/navbar.tsx index 13cd94f..e6822e7 100644 --- a/libs/ui/src/organisms/navbar/navbar.tsx +++ b/libs/ui/src/organisms/navbar/navbar.tsx @@ -7,74 +7,75 @@ export const Navbar: FC = (): ReactElement => { const [isDropdownOpen, setDropdownOpen] = useState(false); return ( -
-
-
- IMPHNEN Logo -
- - -
-
+ +
+ + ); }; diff --git a/package.json b/package.json index adfbc0e..4c65385 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "gacha:dev": "nx serve gacha", "gacha:build": "nx build gacha", "gacha:prod": "serve ./dist/apps/gacha", + "backoffice:dev": "nx serve backoffice", + "backoffice:build": "nx build backoffice", + "backoffice:prod": "serve ./dist/apps/gacha", "dimentorin:dev": "nx serve dimentorin", "dimentorin:build": "nx build dimentorin", "dimentorin:prod": "serve ./dist/apps/dimentorin",