[init] Clinic AD Home 생성
commit
3d07b78160
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
# React + TypeScript + Vite
|
||||||
|
|
||||||
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
|
||||||
|
|
||||||
|
## React Compiler
|
||||||
|
|
||||||
|
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
|
||||||
|
|
||||||
|
## Expanding the ESLint configuration
|
||||||
|
|
||||||
|
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default defineConfig([
|
||||||
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
extends: [
|
||||||
|
// Other configs...
|
||||||
|
|
||||||
|
// Remove tseslint.configs.recommended and replace with this
|
||||||
|
tseslint.configs.recommendedTypeChecked,
|
||||||
|
// Alternatively, use this for stricter rules
|
||||||
|
tseslint.configs.strictTypeChecked,
|
||||||
|
// Optionally, add this for stylistic rules
|
||||||
|
tseslint.configs.stylisticTypeChecked,
|
||||||
|
|
||||||
|
// Other configs...
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
// other options...
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// eslint.config.js
|
||||||
|
import reactX from 'eslint-plugin-react-x'
|
||||||
|
import reactDom from 'eslint-plugin-react-dom'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
extends: [
|
||||||
|
// Other configs...
|
||||||
|
// Enable lint rules for React
|
||||||
|
reactX.configs['recommended-typescript'],
|
||||||
|
// Enable lint rules for React DOM
|
||||||
|
reactDom.configs.recommended,
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
// other options...
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
extends: [
|
||||||
|
js.configs.recommended,
|
||||||
|
tseslint.configs.recommended,
|
||||||
|
reactHooks.configs.flat.recommended,
|
||||||
|
reactRefresh.configs.vite,
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="ko">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>fe</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/app/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "fe",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc -b && vite build",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/react-query": "^5.90.21",
|
||||||
|
"axios": "^1.13.6",
|
||||||
|
"react": "^19.2.4",
|
||||||
|
"react-dom": "^19.2.4",
|
||||||
|
"react-router-dom": "^7.13.1",
|
||||||
|
"zustand": "^5.0.11"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.39.4",
|
||||||
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
|
"@types/node": "^24.12.0",
|
||||||
|
"@types/react": "^19.2.14",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"@vitejs/plugin-react": "^5.2.0",
|
||||||
|
"eslint": "^9.39.4",
|
||||||
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
|
"eslint-plugin-react-refresh": "^0.5.2",
|
||||||
|
"globals": "^17.4.0",
|
||||||
|
"tailwindcss": "^4.2.1",
|
||||||
|
"typescript": "~5.9.3",
|
||||||
|
"typescript-eslint": "^8.56.1",
|
||||||
|
"vite": "^7.3.1",
|
||||||
|
"vite-tsconfig-paths": "^6.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.3 KiB |
|
|
@ -0,0 +1,24 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<symbol id="bluesky-icon" viewBox="0 0 16 17">
|
||||||
|
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
|
||||||
|
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="discord-icon" viewBox="0 0 20 19">
|
||||||
|
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="documentation-icon" viewBox="0 0 21 20">
|
||||||
|
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
|
||||||
|
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
|
||||||
|
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="github-icon" viewBox="0 0 19 19">
|
||||||
|
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="social-icon" viewBox="0 0 20 20">
|
||||||
|
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
|
||||||
|
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="x-icon" viewBox="0 0 19 19">
|
||||||
|
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.9 KiB |
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Routes, Route } from "react-router-dom";
|
||||||
|
// layouts
|
||||||
|
import MainLayout from "@/layouts/MainLayout";
|
||||||
|
|
||||||
|
// pages
|
||||||
|
import { Home } from "@/pages/Home";
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route element={<MainLayout />}>
|
||||||
|
<Route index element={<Home />} />
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap');
|
||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--Color-Neutral-90: #151515;
|
||||||
|
--Color-Neutral-80: #303030;
|
||||||
|
--Color-Neutral-70: #606060;
|
||||||
|
--Color-Neutral-60: #808080;
|
||||||
|
--Color-Neutral-40: #DADBDE;
|
||||||
|
--Color-Neutral-30: #EAEBEF;
|
||||||
|
--Color-Neutral-20: #F0F1F4;
|
||||||
|
--Color-Neutral-10: #F7F8FA;
|
||||||
|
--Color-Neutral-00: #FFF;
|
||||||
|
--Color-Negative: #E71C3B;
|
||||||
|
--Color-Negative-Hover: #C21630;
|
||||||
|
--Color-B: #4880EF;
|
||||||
|
--Color-B-Hover: #3568C5;
|
||||||
|
|
||||||
|
--font-family-primary: 'Pretendard', system-ui, sans-serif;
|
||||||
|
--font-family-display: 'Playfair Display', serif;
|
||||||
|
--font-family-inter: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
font-family: var(--font-family-inter);
|
||||||
|
font-style: normal;
|
||||||
|
line-height: normal;
|
||||||
|
min-height: 100dvh;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { StrictMode } from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import { QueryProvider } from './providers/QueryProvider';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
import App from './App.tsx';
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
<StrictMode>
|
||||||
|
<BrowserRouter>
|
||||||
|
<QueryProvider>
|
||||||
|
<App />
|
||||||
|
</QueryProvider>
|
||||||
|
</BrowserRouter>
|
||||||
|
</StrictMode>,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
export function QueryProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
const [queryClient] = useState(
|
||||||
|
() =>
|
||||||
|
new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
staleTime: 60 * 1000, // 1분 동안 데이터를 fresh로 유지
|
||||||
|
gcTime: 5 * 60 * 1000, // 5분 동안 캐시 유지 후 가비지 컬렉션
|
||||||
|
refetchOnWindowFocus: false, // 윈도우 포커스 시 자동 재요청 비활성화
|
||||||
|
retry: 1, // 요청 실패 시 1회 재시도
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
{children}
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 185 KiB |
|
|
@ -0,0 +1,13 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||||
|
<g clip-path="url(#clip0_2455_185)">
|
||||||
|
<path d="M7.34467 1.87599C7.37324 1.72306 7.45439 1.58493 7.57407 1.48553C7.69375 1.38614 7.84443 1.33173 8 1.33173C8.15558 1.33173 8.30626 1.38614 8.42594 1.48553C8.54562 1.58493 8.62677 1.72306 8.65534 1.87599L9.356 5.58132C9.40577 5.84475 9.53379 6.08707 9.72336 6.27664C9.91293 6.46621 10.1552 6.59423 10.4187 6.64399L14.124 7.34466C14.2769 7.37322 14.4151 7.45437 14.5145 7.57405C14.6139 7.69374 14.6683 7.84441 14.6683 7.99999C14.6683 8.15557 14.6139 8.30624 14.5145 8.42592C14.4151 8.54561 14.2769 8.62676 14.124 8.65532L10.4187 9.35599C10.1552 9.40575 9.91293 9.53377 9.72336 9.72334C9.53379 9.91291 9.40577 10.1552 9.356 10.4187L8.65534 14.124C8.62677 14.2769 8.54562 14.415 8.42594 14.5144C8.30626 14.6138 8.15558 14.6683 8 14.6683C7.84443 14.6683 7.69375 14.6138 7.57407 14.5144C7.45439 14.415 7.37324 14.2769 7.34467 14.124L6.644 10.4187C6.59424 10.1552 6.46622 9.91291 6.27665 9.72334C6.08708 9.53377 5.84477 9.40575 5.58134 9.35599L1.876 8.65532C1.72307 8.62676 1.58495 8.54561 1.48555 8.42592C1.38615 8.30624 1.33174 8.15557 1.33174 7.99999C1.33174 7.84441 1.38615 7.69374 1.48555 7.57405C1.58495 7.45437 1.72307 7.37322 1.876 7.34466L5.58134 6.64399C5.84477 6.59423 6.08708 6.46621 6.27665 6.27664C6.46622 6.08707 6.59424 5.84475 6.644 5.58132L7.34467 1.87599Z" stroke="#6C5CE7" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M13.3333 1.33334V4.00001" stroke="#6C5CE7" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M14.6667 2.66666H12" stroke="#6C5CE7" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M2.66666 14.6667C3.40304 14.6667 3.99999 14.0697 3.99999 13.3333C3.99999 12.597 3.40304 12 2.66666 12C1.93028 12 1.33333 12.597 1.33333 13.3333C1.33333 14.0697 1.93028 14.6667 2.66666 14.6667Z" stroke="#6C5CE7" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_2455_185">
|
||||||
|
<rect width="16" height="16" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,13 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||||
|
<g clip-path="url(#clip0_2455_340)">
|
||||||
|
<path d="M7.34467 1.87605C7.37323 1.72312 7.45438 1.58499 7.57407 1.4856C7.69375 1.3862 7.84442 1.33179 8 1.33179C8.15558 1.33179 8.30625 1.3862 8.42594 1.4856C8.54562 1.58499 8.62677 1.72312 8.65533 1.87605L9.356 5.58138C9.40576 5.84482 9.53378 6.08713 9.72335 6.2767C9.91292 6.46627 10.1552 6.59429 10.4187 6.64405L14.124 7.34472C14.2769 7.37328 14.4151 7.45443 14.5145 7.57412C14.6139 7.6938 14.6683 7.84447 14.6683 8.00005C14.6683 8.15563 14.6139 8.3063 14.5145 8.42599C14.4151 8.54567 14.2769 8.62682 14.124 8.65538L10.4187 9.35605C10.1552 9.40581 9.91292 9.53383 9.72335 9.7234C9.53378 9.91297 9.40576 10.1553 9.356 10.4187L8.65533 14.1241C8.62677 14.277 8.54562 14.4151 8.42594 14.5145C8.30625 14.6139 8.15558 14.6683 8 14.6683C7.84442 14.6683 7.69375 14.6139 7.57407 14.5145C7.45438 14.4151 7.37323 14.277 7.34467 14.1241L6.644 10.4187C6.59424 10.1553 6.46622 9.91297 6.27665 9.7234C6.08708 9.53383 5.84477 9.40581 5.58133 9.35605L1.876 8.65538C1.72307 8.62682 1.58494 8.54567 1.48555 8.42599C1.38615 8.3063 1.33174 8.15563 1.33174 8.00005C1.33174 7.84447 1.38615 7.6938 1.48555 7.57412C1.58494 7.45443 1.72307 7.37328 1.876 7.34472L5.58133 6.64405C5.84477 6.59429 6.08708 6.46627 6.27665 6.2767C6.46622 6.08713 6.59424 5.84482 6.644 5.58138L7.34467 1.87605Z" stroke="#DAB2FF" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M13.3333 1.33325V3.99992" stroke="#DAB2FF" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M14.6667 2.66675H12" stroke="#DAB2FF" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M2.66667 14.6667C3.40305 14.6667 4 14.0697 4 13.3333C4 12.597 3.40305 12 2.66667 12C1.93029 12 1.33333 12.597 1.33333 13.3333C1.33333 14.0697 1.93029 14.6667 2.66667 14.6667Z" stroke="#DAB2FF" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_2455_340">
|
||||||
|
<rect width="16" height="16" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,35 @@
|
||||||
|
export function CTASection() {
|
||||||
|
return (
|
||||||
|
<div className="relative flex flex-col items-center justify-center py-[96px] h-[512px] bg-[#0A1128] overflow-hidden">
|
||||||
|
|
||||||
|
{/* 배경 글로우 원 */}
|
||||||
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] rounded-full bg-[rgba(173,70,255,0.20)] blur-[50px] pointer-events-none" />
|
||||||
|
|
||||||
|
{/* 타이틀 */}
|
||||||
|
<div className="relative flex flex-col items-center gap-4 pb-16">
|
||||||
|
<div
|
||||||
|
className="bg-gradient-to-r from-[#FFF3EB] via-[#E4CFFF] to-[#F5F9FF] bg-clip-text text-transparent text-[48px] font-bold leading-[48px]"
|
||||||
|
style={{ fontFamily: "var(--font-family-display)" }}
|
||||||
|
>
|
||||||
|
Ready to Transform Your Marketing?
|
||||||
|
</div>
|
||||||
|
<div className="text-[#E9D4FF] text-[18px] leading-[29.25px] break-keep">
|
||||||
|
URL 하나로 시작하는 완벽한 마케팅 자동화. 지금 바로 무료 진단을 받아보세요.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 버튼 */}
|
||||||
|
<div className="relative flex flex-col gap-4 w-[448px]">
|
||||||
|
<button className="flex items-center justify-center w-full h-[56px] rounded-full border border-white/20 bg-gradient-to-r from-white via-[#E4CFFF] to-[#F5F9FF] shadow text-[#0A1128]/60 text-base font-medium cursor-pointer">
|
||||||
|
Enter Your URL
|
||||||
|
</button>
|
||||||
|
<button className="flex items-center justify-center w-full h-[56px] bg-gradient-to-r from-[#4F1DA1] to-[#021341] rounded-[999px] text-white text-base font-medium leading-6 cursor-pointer">
|
||||||
|
Analyze ➜
|
||||||
|
</button>
|
||||||
|
<div className="text-[#E9D4FF]/80 text-center text-sm leading-5">
|
||||||
|
네이버 블로그, 플레이스, 소셜미디어 종합 분석 리포트 받아보기
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import bg from "@/assets/home/bg_hero.png";
|
||||||
|
import twinkle from "@/assets/home/twinkle.svg";
|
||||||
|
|
||||||
|
export function HeroSection() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center py-[140px] bg-cover bg-center" style={{ backgroundImage: `url(${bg})` }}>
|
||||||
|
<div className="flex flex-col items-center gap-6">
|
||||||
|
|
||||||
|
{/* 배지 */}
|
||||||
|
<div className="flex items-center gap-2 bg-transparent shadow rounded-[999px] px-4 py-2">
|
||||||
|
<img src={twinkle} alt="twinkle" />
|
||||||
|
<div className="text-[#314158] text-center text-xl font-medium leading-5">
|
||||||
|
Infinite Marketing for Premium Medical Business & Marketing Agency
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 타이틀 */}
|
||||||
|
<div className="flex flex-col items-center gap-2">
|
||||||
|
<div
|
||||||
|
className="text-[#0A1128] text-center text-[72px] font-bold leading-[79.2px] tracking-[-1.8px]"
|
||||||
|
style={{ fontFamily: "var(--font-family-display)" }}
|
||||||
|
>
|
||||||
|
Marketing becomes a
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="bg-gradient-to-r from-[#0A1128] to-[#3B5998] bg-clip-text text-transparent text-[72px] font-bold leading-[96px] tracking-[-1.8px]"
|
||||||
|
style={{ fontFamily: "var(--font-family-display)" }}
|
||||||
|
>
|
||||||
|
self-improving engine.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 설명 */}
|
||||||
|
<div className="flex flex-col items-center mb-4 text-[#45556C] text-center text-xl font-normal leading-[32.5px]">
|
||||||
|
<p>AI가 콘텐츠를 만들고 데이터가 마케팅을 개선합니다.</p>
|
||||||
|
<p>콘텐츠 기획, 생성, 영상 제작, 채널 배포, 데이터 분석을 하나의 자동화 엔진으로.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 버튼 */}
|
||||||
|
<div className="flex flex-col items-center gap-4">
|
||||||
|
<button className="flex items-center justify-center w-full h-[56px] border border-[#E2E8F0] bg-white shadow rounded-[999px] text-[#90A1B9] text-base font-medium cursor-pointer hover:bg-[#F1F5F9] hover:text-[#64748B]">
|
||||||
|
Enter Your URL
|
||||||
|
</button>
|
||||||
|
<button className="flex items-center justify-center w-full h-[56px] bg-gradient-to-r from-[#4F1DA1] to-[#021341] rounded-[999px] text-white text-base font-medium leading-6 cursor-pointer">
|
||||||
|
Analyze ➜
|
||||||
|
</button>
|
||||||
|
<div className="text-[#62748E] text-center text-lg font-normal leading-4">
|
||||||
|
네이버 블로그, 플레이스, 소셜미디어 등 Online Presence 종합 분석 리포트를 제공합니다.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
const PROBLEMS = [
|
||||||
|
{
|
||||||
|
title: "콘텐츠 생산의 한계",
|
||||||
|
description: "블로그, SEO, 유튜브, 숏폼 등 지속적 생산이 필요하지만 인력과 비용, 시간 부족으로 제한됩니다.",
|
||||||
|
gradient: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "영상 콘텐츠 제작 비용",
|
||||||
|
description: "영상은 중요하지만 촬영, 편집, 기획 비용이 높습니다. 특히 숏폼 콘텐츠는 지속적인 제작이 어렵습니다.",
|
||||||
|
gradient: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "데이터 기반의 마케팅 부족",
|
||||||
|
description: "어떤 콘텐츠가 효과적인지, 어떤 키워드가 유입을 만드는지, 어떤 채널이 성과가 좋은지 알기 어렵고, 각 플랫폼들의 데이터들을 한눈에 파악할 수 없습니다.",
|
||||||
|
gradient: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export function ProblemSection() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center py-[96px] bg-[#F8FAFC]">
|
||||||
|
<div className="flex flex-col items-center gap-4">
|
||||||
|
<div
|
||||||
|
className="text-[#0A1128] text-center text-[48px] font-bold leading-[48px]"
|
||||||
|
style={{ fontFamily: "var(--font-family-display)" }}
|
||||||
|
>
|
||||||
|
Premium Medical Marketing is Hard
|
||||||
|
</div>
|
||||||
|
<div className="text-[#45556C] text-[18px] leading-[29.25px]">
|
||||||
|
병원 마케팅이 직면한 3가지 핵심 과제
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-6">
|
||||||
|
{PROBLEMS.map(({ title, description, gradient }, index) => (
|
||||||
|
<div key={index} className="flex flex-col w-[368px] h-[227px] gap-4 rounded-2xl bg-white shadow items-start justify-center px-10 py-12">
|
||||||
|
<div className={gradient ? "bg-gradient-to-r from-[#4F39F6] to-[#9810FA] bg-clip-text text-transparent text-[24px] font-bold leading-[32px]" : "text-[#0A1128] text-[24px] font-bold leading-[32px]"}>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
<div className="text-[#45556C] text-[16px] leading-[20px] break-keep">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
import twinkle_pink from "@/assets/home/twinkle_pink.svg";
|
||||||
|
|
||||||
|
const AGDP_ITEMS = [
|
||||||
|
{ letter: "G", label: "Generation", angle: 0 },
|
||||||
|
{ letter: "D", label: "Distribution", angle: 90 },
|
||||||
|
{ letter: "P", label: "Performance", angle: 180 },
|
||||||
|
{ letter: "A", label: "Analysis", angle: 270 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const REWARD_SIGNAL_CONFIG = {
|
||||||
|
text: "← REWARD SIGNAL",
|
||||||
|
startAngle: 158,
|
||||||
|
endAngle: 110,
|
||||||
|
radius: 200,
|
||||||
|
cx: 300,
|
||||||
|
cy: 300,
|
||||||
|
}
|
||||||
|
|
||||||
|
function RewardSignalText() {
|
||||||
|
const { text, startAngle, endAngle, radius, cx, cy } = REWARD_SIGNAL_CONFIG
|
||||||
|
const chars = text.split("")
|
||||||
|
const angleStep = (endAngle - startAngle) / (chars.length - 1)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg className="absolute w-full h-full" viewBox="0 0 600 600">
|
||||||
|
{chars.map((char, i) => {
|
||||||
|
const angle = (startAngle + i * angleStep) * (Math.PI / 180)
|
||||||
|
return (
|
||||||
|
<text
|
||||||
|
key={i}
|
||||||
|
x={cx + radius * Math.cos(angle)}
|
||||||
|
y={cy + radius * Math.sin(angle)}
|
||||||
|
fontSize="11"
|
||||||
|
fill="rgba(255,255,255,0.4)"
|
||||||
|
textAnchor="middle"
|
||||||
|
dominantBaseline="middle"
|
||||||
|
>
|
||||||
|
{char}
|
||||||
|
</text>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ProcessSection() {
|
||||||
|
return (
|
||||||
|
<div className="relative flex flex-col items-center justify-center py-[128px] bg-[#0A1128] overflow-hidden">
|
||||||
|
|
||||||
|
{/* 배경 글로우 원 */}
|
||||||
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] rounded-full bg-[rgba(173,70,255,0.20)] blur-[50px] pointer-events-none" />
|
||||||
|
|
||||||
|
<div className="relative flex flex-col items-center">
|
||||||
|
|
||||||
|
{/* 배지 */}
|
||||||
|
<div className="flex mb-8 items-center gap-2 border border-white/20 bg-white/10 backdrop-blur-[4px] rounded-[999px] px-4 py-2">
|
||||||
|
<img src={twinkle_pink} alt="twinkle" />
|
||||||
|
<div className="text-[#F3E8FF] text-center text-xl font-medium leading-5">AI Marketing Engine</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 타이틀 */}
|
||||||
|
<div
|
||||||
|
className="bg-gradient-to-r from-[#FFF3EB] via-[#E4CFFF] to-[#F5F9FF] bg-clip-text text-transparent text-[60px] font-bold leading-[75px] mb-6"
|
||||||
|
style={{ fontFamily: "var(--font-family-display)" }}
|
||||||
|
>
|
||||||
|
Infinite Marketing Engine
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 설명 */}
|
||||||
|
<div className="flex flex-col items-center gap-1 w-[900px]" style={{ fontFamily: "var(--font-family-inter)" }}>
|
||||||
|
<span className="text-white text-2xl leading-[39px]">
|
||||||
|
Infinite Marketing for Premium Medical Business & Marketing Agency
|
||||||
|
</span>
|
||||||
|
<span className="text-[#CAD5E2] text-2xl font-light leading-[39px] break-keep">
|
||||||
|
Infinite Marketing은 Premium Medical Business와 Marketing Agency를 위한 AI Marketing Automation Platform입니다.
|
||||||
|
CLINICAD는 마케팅 분석, 콘텐츠 생성, 영상 콘텐츠 제작, 채널 배포, 성과 분석과 피드백 적용을 하나의 Self-Improving Marketing Engine으로 제공합니다.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 원 레이어 */}
|
||||||
|
<div className="relative w-[600px] h-[600px] flex items-center justify-center mt-10 mb-16">
|
||||||
|
<div className="absolute w-[310px] h-[310px] rounded-full border border-dashed border-white/40" />
|
||||||
|
|
||||||
|
<RewardSignalText />
|
||||||
|
|
||||||
|
{/* 중앙 원 */}
|
||||||
|
<div className="z-10 w-[224px] h-[224px] rounded-full bg-[#0D0A2E] border border-white/10 flex flex-col items-center justify-center gap-1">
|
||||||
|
<span className="text-[#E9D4FF] text-2xl font-medium leading-[30px]">AGDP</span>
|
||||||
|
<span className="text-white text-[30px] font-bold leading-[37.5px] text-center" style={{ fontFamily: "var(--font-family-display)" }}>
|
||||||
|
Infinite<br />Marketing
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 위성 원 */}
|
||||||
|
{AGDP_ITEMS.map(({ letter, label, angle }) => (
|
||||||
|
<div
|
||||||
|
key={letter}
|
||||||
|
className="absolute flex flex-col items-center gap-2"
|
||||||
|
style={{ transform: `rotate(${angle}deg) translateY(-230px) rotate(-${angle}deg)` }}
|
||||||
|
>
|
||||||
|
<div className="w-20 h-20 rounded-full flex items-center justify-center border border-[#AD46FF]/30 bg-[#0A1128]/80 shadow-[0_0_20px_0_rgba(168,85,247,0.15)] backdrop-blur-[4px] text-[#DAB2FF] text-[36px] font-bold leading-10">
|
||||||
|
{letter}
|
||||||
|
</div>
|
||||||
|
<span className="text-[#E9D4FF] text-2xl font-medium leading-[30px]">{label}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* AGDP Cycle 설명 */}
|
||||||
|
<div className="flex max-w-[780px] px-4 py-6 rounded-2xl border border-white/10 bg-white/5 backdrop-blur-[4px]">
|
||||||
|
<span className="text-[#DAB2FF] text-base font-bold leading-6 whitespace-nowrap">AGDP Cycle: </span>
|
||||||
|
<div className="text-center text-base leading-6 break-keep">
|
||||||
|
<span className="text-[#CAD5E2]">분석(Analysis) → 생성(Generation) → 배포(Distribution) → 성과(Performance)의 무한 루프를 통해 콘텐츠 품질(CTR)을 </span>
|
||||||
|
<span className="text-white">자율 최적화</span>
|
||||||
|
<span className="text-[#CAD5E2]">합니다.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
const SOLUTION_CARDS = [
|
||||||
|
{
|
||||||
|
title: "Premium Medical Business",
|
||||||
|
description: "고객 LTV가 높고 브랜드 경쟁이 심해 콘텐츠 마케팅이 필수적인 프리미엄 의료 서비스 제공 병원",
|
||||||
|
items: ["피부과", "성형외과", "치과", "안과", "헬스케어 클리닉", "피트니스"],
|
||||||
|
gradient: "from-[#F8FAFC]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Medical Marketing Agency",
|
||||||
|
description: "콘텐츠 제작 비용 부담과 인력 의존도가 높고 영상 제작 생산성 개선이 필요한 병원 마케팅 대행사",
|
||||||
|
items: ["병원 마케팅 대행사", "콘텐츠 마케팅 Agency", "영상 마케팅 Agency", "광고 운영 대행사"],
|
||||||
|
gradient: "from-[#FAF5FF]",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export function SolutionSection() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center py-[96px]">
|
||||||
|
<div className="flex flex-col gap-16">
|
||||||
|
<div className="flex flex-col items-center gap-4">
|
||||||
|
<div
|
||||||
|
className="text-[#0A1128] text-center text-[48px] font-bold leading-[48px]"
|
||||||
|
style={{ fontFamily: "var(--font-family-display)" }}
|
||||||
|
>
|
||||||
|
Who is Infinite Marketing for
|
||||||
|
</div>
|
||||||
|
<div>프리미엄 의료 서비스와 전문 마케팅 에이전시를 위한 최적의 솔루션</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-6">
|
||||||
|
{SOLUTION_CARDS.map(({ title, description, items, gradient }, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`flex flex-col gap-6 p-[48px] w-[560px] rounded-2xl border border-white/20 bg-gradient-to-br ${gradient} to-white shadow-[0_8px_30px_0_rgba(0,0,0,0.04)] backdrop-blur-[6px]`}
|
||||||
|
>
|
||||||
|
<div className="text-[#0A1128] text-[48px] font-bold leading-[60px]" style={{ fontFamily: "var(--font-family-display)" }}>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
<div className="text-[#45556C] text-[18px] leading-[29.25px] break-keep">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-3 w-full pr-20">
|
||||||
|
{items.map((item, i) => (
|
||||||
|
<div key={i} className="flex items-center justify-center h-[50px] px-3 py-5 rounded-[16px] shadow whitespace-nowrap bg-white">
|
||||||
|
{item}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
import bg from "@/assets/home/bg_system.png";
|
||||||
|
|
||||||
|
const CORE_MODULES = [
|
||||||
|
{ index: 1, title: "Marketing Intelligence", description: ["브랜딩, 마케팅 현황 분석", "타겟 고객 분석", "키워드 분석", "경쟁 및 포지셔닝 분석"], footer: "SEO 전략 & 채널별 콘텐츠 기획", angle: 0 },
|
||||||
|
{ index: 2, title: "Content Creation", description: ["블로그 콘텐츠 생성", "SEO 콘텐츠 생성", "SNS 콘텐츠 생성", "마케팅 카피 생성"], footer: "Human-in-the loop 프로세스", angle: 78 },
|
||||||
|
{ index: 3, title: "Video Automation", description: ["블로그 → 영상 변환", "숏폼 콘텐츠 생성", "유튜브 콘텐츠 제작", "SNS 영상 제작"], footer: "멀티모달 AI 엔진: 영상 + 음악 + 카피", angle: 144 },
|
||||||
|
{ index: 4, title: "Distribution Engine", description: ["블로그 게시", "SNS 자동 게시", "유튜브 업로드", "콘텐츠 일정 관리"], footer: "SEO, AEO 자동 최적화", angle: 216 },
|
||||||
|
{ index: 5, title: "Performance Intelligence", description: ["SEO 성과 분석", "콘텐츠 성과 분석", "채널 성과 분석", "AI 콘텐츠 개선 전략 추천"], footer: "데이터 기반 효과 검증", angle: 283 },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function SystemSection() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center py-[128px] bg-cover bg-center" style={{ backgroundImage: `url(${bg})` }}>
|
||||||
|
<div className="flex flex-col items-center justify-center gap-6 mb-[96px]">
|
||||||
|
<div className="text-[#0A1128] text-center text-[48px] font-bold leading-[48px]" style={{ fontFamily: "var(--font-family-display)" }}>
|
||||||
|
Core Modules
|
||||||
|
</div>
|
||||||
|
<div className="text-[#45556C] text-[18px] font-normal leading-[29.25px]">
|
||||||
|
성능 개선 반영 자율 순환 마케팅 시스템
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative w-[1100px] h-[1100px] flex items-center justify-center">
|
||||||
|
<div className="absolute z-10 text-[#1D293D] text-center text-[31px] font-bold leading-[38.75px] mb-30" style={{ fontFamily: "var(--font-family-display)" }}>
|
||||||
|
Self-Improving<br />Growth Engine
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{CORE_MODULES.map(({ index, title, description, footer, angle }) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="absolute flex flex-col items-start p-[25px] rounded-[16px] bg-white shadow"
|
||||||
|
style={{ transform: `rotate(${angle}deg) translateY(-430px) rotate(-${angle}deg)` }}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3 mb-5">
|
||||||
|
<div className="flex items-center justify-center w-10 h-10 rounded-[16px] bg-[#021341] text-white text-lg font-bold leading-7">
|
||||||
|
{index}
|
||||||
|
</div>
|
||||||
|
<div className="text-[#1D293D] text-[24px] font-bold leading-[25px] tracking-[-0.5px]" style={{ fontFamily: "var(--font-family-display)" }}>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1 mb-6">
|
||||||
|
{description.map((desc) => (
|
||||||
|
<div key={desc} className="text-[#45556C] text-[18px] leading-[29.25px] break-keep">{desc}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-[#F1F5F9] w-full h-[1px] mb-4" />
|
||||||
|
<div className="text-[#4F39F6] text-xl font-bold leading-[22.5px] tracking-[-0.375px] break-keep">{footer}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
const CheckIcon = ({ fill }: { fill: string }) => (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke={fill} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
|
<path d="M9 12L11 14L15 10" stroke={fill} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
const USE_CASES = [
|
||||||
|
{
|
||||||
|
title: "Premium Medical Business",
|
||||||
|
descriptions: ["SEO 콘텐츠 자동 생산으로 검색 상위 노출 달성", "비용 부담 없이 고품질 영상 콘텐츠 대량 확대", "자연 검색 유입 증가로 인한 환자 전환율 상승"],
|
||||||
|
color: "#2B7FFF",
|
||||||
|
gradient: "from-[#EFF6FF]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Marketing Agency",
|
||||||
|
descriptions: ["AI 기반 콘텐츠 제작 자동화로 생산성 극대화", "블로그 텍스트 기반 영상 제작 자동화로 리소스 절감", "다수 클라이언트 계정의 통합 운영 및 효율화"],
|
||||||
|
color: "#AD46FF",
|
||||||
|
gradient: "from-[#FAF5FF]",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export function UseCaseSection() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center py-[96px] bg-white">
|
||||||
|
<div className="flex flex-col items-center gap-4 pb-16">
|
||||||
|
<div className="text-[#0A1128] text-center text-[48px] font-bold leading-[48px]" style={{ fontFamily: "var(--font-family-display)" }}>
|
||||||
|
Use Cases
|
||||||
|
</div>
|
||||||
|
<div className="text-[#45556C] text-[18px] leading-[29.25px] break-keep">
|
||||||
|
Infinite Marketing이 만드는 실질적인 변화를 확인해보세요!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-6">
|
||||||
|
{USE_CASES.map(({ title, descriptions, color, gradient }, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`flex flex-col w-[560px] h-[227px] p-10 items-start justify-center rounded-2xl border border-white/20 bg-gradient-to-br ${gradient} to-white shadow-[0_8px_30px_0_rgba(0,0,0,0.04)] backdrop-blur-[6px]`}
|
||||||
|
>
|
||||||
|
<div className="text-[#0A1128] text-[24px] font-bold leading-[32px] pb-[22.5px]" style={{ fontFamily: "var(--font-family-display)" }}>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{descriptions.map((desc, i) => (
|
||||||
|
<div key={i} className="flex items-center gap-2">
|
||||||
|
<CheckIcon fill={color} /> {desc}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { HeroSection } from "./HeroSection";
|
||||||
|
import { SolutionSection } from "./SolutionSection";
|
||||||
|
import { ProblemSection } from "./ProblemSection";
|
||||||
|
import { ProcessSection } from "./ProcessSection";
|
||||||
|
import { SystemSection } from "./SystemSection";
|
||||||
|
import { UseCaseSection } from "./UseCaseSection";
|
||||||
|
import { CTASection } from "./CTASection";
|
||||||
|
|
||||||
|
export {
|
||||||
|
HeroSection,
|
||||||
|
SolutionSection,
|
||||||
|
ProblemSection,
|
||||||
|
ProcessSection,
|
||||||
|
SystemSection,
|
||||||
|
UseCaseSection,
|
||||||
|
CTASection,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
export function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="flex w-full items-center justify-between px-[316px] py-[48px]">
|
||||||
|
<div
|
||||||
|
className="text-[#0A1128] text-[30px] not-italic font-bold leading-[32px] tracking-[-0.6px]"
|
||||||
|
>
|
||||||
|
CLINICAD
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="text-[#62748E] font-inter text-sm font-normal leading-5"
|
||||||
|
>
|
||||||
|
© 2026 CLINICAD. All rights reserved. Infinite Marketing for Premium Medical Business & Marketing Agency
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-6">
|
||||||
|
<span className="text-[#62748E] font-inter text-sm font-normal leading-5">Privacy Policy</span>
|
||||||
|
<span className="text-[#62748E] font-inter text-sm font-normal leading-5">Terms of Service</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
const MENUS = [
|
||||||
|
{ name: "Solution", href: "/" },
|
||||||
|
{ name: "Modules", href: "/" },
|
||||||
|
{ name: "Use Cases", href: "/" },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function GNB() {
|
||||||
|
return (
|
||||||
|
<nav className="w-full h-14 flex items-center justify-between px-[316px]" style={{ background: "var(--Color-Neutral-00)" }}>
|
||||||
|
|
||||||
|
{/* 로고 */}
|
||||||
|
<span
|
||||||
|
onClick={() => window.location.href = "/"}
|
||||||
|
className="text-[32px] font-bold leading-[32px] tracking-[-0.6px] ml-6 cursor-pointer"
|
||||||
|
style={{ color: "#0A1128", fontFamily: "var(--font-family-display)" }}
|
||||||
|
>
|
||||||
|
CLINAD
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* 메뉴 */}
|
||||||
|
<ul className="flex items-center gap-8">
|
||||||
|
{MENUS.map(({ name }, index) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className="text-sm font-medium leading-5 cursor-pointer whitespace-nowrap"
|
||||||
|
style={{ color: "var(--Color-Neutral-70)", fontFamily: "var(--font-family-inter)" }}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* 로그인 버튼 */}
|
||||||
|
<button
|
||||||
|
onClick={() => window.location.href = "/login"}
|
||||||
|
className="flex items-center justify-center w-[85px] h-[40px] shadow rounded-[99px] mr-6 bg-gradient-to-r from-[#4F1DA1] to-[#021341] text-white text-sm font-medium leading-5 cursor-pointer"
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
import { GNB } from "@/layouts/GNB";
|
||||||
|
import { Footer } from "@/layouts/Footer";
|
||||||
|
|
||||||
|
export default function MainLayout() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col min-h-screen">
|
||||||
|
<header className="flex-none"><GNB /></header>
|
||||||
|
<main className="flex-grow"><Outlet /></main>
|
||||||
|
<footer className="flex-none"><Footer /></footer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { HeroSection } from "@/features/home/ui";
|
||||||
|
import { SolutionSection } from "@/features/home/ui";
|
||||||
|
import { ProblemSection } from "@/features/home/ui";
|
||||||
|
import { ProcessSection } from "@/features/home/ui";
|
||||||
|
import { SystemSection } from "@/features/home/ui";
|
||||||
|
import { UseCaseSection } from "@/features/home/ui";
|
||||||
|
import { CTASection } from "@/features/home/ui";
|
||||||
|
|
||||||
|
export function Home() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col w-full">
|
||||||
|
<HeroSection />
|
||||||
|
<SolutionSection />
|
||||||
|
<ProblemSection />
|
||||||
|
<ProcessSection />
|
||||||
|
<SystemSection />
|
||||||
|
<UseCaseSection />
|
||||||
|
<CTASection />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2023",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"types": ["vite/client"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"erasableSyntaxOnly": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
|
||||||
|
/* alias 설정 */
|
||||||
|
"baseUrl": "src",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.app.json" },
|
||||||
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"target": "ES2023",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"types": ["node"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"erasableSyntaxOnly": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
|
||||||
|
/* alias 설정 */
|
||||||
|
"baseUrl": "src",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react(), tsconfigPaths(), tailwindcss()],
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue