chore: test deploy snapshot
This commit is contained in:
434
src/views/play/apps/template1/Template1PlayAppView.tsx
Normal file
434
src/views/play/apps/template1/Template1PlayAppView.tsx
Normal file
@@ -0,0 +1,434 @@
|
||||
import {
|
||||
AppstoreOutlined,
|
||||
AppstoreAddOutlined,
|
||||
BellOutlined,
|
||||
CheckCircleOutlined,
|
||||
CompassOutlined,
|
||||
LogoutOutlined,
|
||||
CloseOutlined,
|
||||
SettingOutlined,
|
||||
ThunderboltOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useMemo, useState, type ReactNode } from 'react';
|
||||
import './Template1PlayAppView.css';
|
||||
|
||||
type Template1PlayAppViewProps = {
|
||||
onBack: () => void;
|
||||
launchContext?: 'direct' | 'embedded';
|
||||
};
|
||||
|
||||
type Template1SectionId = 'home' | 'projects' | 'resources' | 'automation';
|
||||
|
||||
type Template1Section = {
|
||||
id: Template1SectionId;
|
||||
label: string;
|
||||
description: string;
|
||||
chips: string[];
|
||||
};
|
||||
|
||||
type Template1MenuItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
icon: ReactNode;
|
||||
screen?: Template1SectionId;
|
||||
actionLabel?: string;
|
||||
actionType?: 'navigate' | 'exit' | 'local';
|
||||
settingAction?: 'theme' | 'notifications' | 'reset-state';
|
||||
};
|
||||
|
||||
const TEMPLATE1_SECTIONS: Template1Section[] = [
|
||||
{
|
||||
id: 'home',
|
||||
label: '홈',
|
||||
description: '빠른 네비게이션으로 주요 기능으로 이동',
|
||||
chips: ['요약', '최근 작업', '바로가기'],
|
||||
},
|
||||
{
|
||||
id: 'projects',
|
||||
label: '작업',
|
||||
description: '프로젝트 관리나 문맥 전환 진입점',
|
||||
chips: ['레이아웃', '요청', '검수'],
|
||||
},
|
||||
{
|
||||
id: 'resources',
|
||||
label: '리소스',
|
||||
description: '문서/데이터/관리 화면 모음',
|
||||
chips: ['문서', '리소스', '로그'],
|
||||
},
|
||||
{
|
||||
id: 'automation',
|
||||
label: '자동화',
|
||||
description: '자동화와 상태 확인 흐름',
|
||||
chips: ['플랜', '체크리스트', '실행 이력'],
|
||||
},
|
||||
];
|
||||
|
||||
const TEMPLATE1_MENUS: Record<Template1SectionId, Template1MenuItem[]> = {
|
||||
home: [
|
||||
{
|
||||
title: '오늘 개요',
|
||||
description: '최근 작업 수와 진행 상태를 한 화면에서 확인',
|
||||
icon: <CompassOutlined />,
|
||||
screen: 'home',
|
||||
actionLabel: '열기',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
{
|
||||
title: '요청 보드',
|
||||
description: '채팅/플랜 타입의 빠른 이동',
|
||||
icon: <ThunderboltOutlined />,
|
||||
screen: 'projects',
|
||||
actionLabel: '이동',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
{
|
||||
title: '앱 바코드',
|
||||
description: '새 앱 연결 전에 기본 레이아웃 점검',
|
||||
icon: <AppstoreAddOutlined />,
|
||||
screen: 'projects',
|
||||
actionLabel: '확인',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
],
|
||||
projects: [
|
||||
{
|
||||
title: 'Layout Editor',
|
||||
description: '레이아웃 편집 흐름 컴포넌트',
|
||||
icon: <CompassOutlined />,
|
||||
screen: 'projects',
|
||||
actionLabel: '열기',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
{
|
||||
title: 'Layout Draw',
|
||||
description: '컴포넌트 샘플 배치 화면',
|
||||
icon: <ThunderboltOutlined />,
|
||||
screen: 'projects',
|
||||
actionLabel: '열기',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
{
|
||||
title: '저장된 레이아웃',
|
||||
description: '저장 기록과 재열기 진입점',
|
||||
icon: <CheckCircleOutlined />,
|
||||
screen: 'projects',
|
||||
actionLabel: '열기',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
],
|
||||
resources: [
|
||||
{
|
||||
title: '문서',
|
||||
description: '문서 목록과 상세 진입 구성',
|
||||
icon: <CompassOutlined />,
|
||||
screen: 'resources',
|
||||
actionLabel: '열기',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
{
|
||||
title: '리소스 관리',
|
||||
description: '공유 리소스 목록과 승인 상태',
|
||||
icon: <AppstoreAddOutlined />,
|
||||
screen: 'resources',
|
||||
actionLabel: '열기',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
{
|
||||
title: '활동 로그',
|
||||
description: '최근 변경 이력 요약',
|
||||
icon: <BellOutlined />,
|
||||
screen: 'automation',
|
||||
actionLabel: '열기',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
],
|
||||
automation: [
|
||||
{
|
||||
title: '자동화',
|
||||
description: 'Plan/작업 진행 목록',
|
||||
icon: <AppstoreAddOutlined />,
|
||||
screen: 'automation',
|
||||
actionLabel: '열기',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
{
|
||||
title: '체크리스트',
|
||||
description: '요청 단계 추적',
|
||||
icon: <CheckCircleOutlined />,
|
||||
screen: 'automation',
|
||||
actionLabel: '확인',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
{
|
||||
title: '에러 로그',
|
||||
description: '실패/경고 대응 화면',
|
||||
icon: <CloseOutlined />,
|
||||
screen: 'automation',
|
||||
actionLabel: '확인',
|
||||
actionType: 'navigate',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const settingsItems: Template1MenuItem[] = [
|
||||
{
|
||||
title: '테마',
|
||||
description: '배경/카드 스타일 상태를 확인',
|
||||
icon: <AppstoreOutlined />,
|
||||
actionType: 'local',
|
||||
settingAction: 'theme',
|
||||
},
|
||||
{
|
||||
title: '알림',
|
||||
description: '알림 영역 설정 진입점',
|
||||
icon: <BellOutlined />,
|
||||
actionType: 'local',
|
||||
settingAction: 'notifications',
|
||||
},
|
||||
{
|
||||
title: '상태 초기화',
|
||||
description: '현재 화면을 홈 상태로 복원',
|
||||
icon: <CheckCircleOutlined />,
|
||||
actionType: 'local',
|
||||
settingAction: 'reset-state',
|
||||
},
|
||||
{
|
||||
title: '앱 종료',
|
||||
description: '설정을 닫고 앱 화면을 종료합니다.',
|
||||
icon: <LogoutOutlined />,
|
||||
actionType: 'exit',
|
||||
},
|
||||
];
|
||||
|
||||
type Template1HomeScreenProps = {
|
||||
items: Pick<Template1MenuItem, 'title' | 'description' | 'icon' | 'actionLabel'> & {
|
||||
chips: string[];
|
||||
screen: Template1SectionId;
|
||||
}[];
|
||||
onItemSelect: (item: Template1MenuItem) => void;
|
||||
};
|
||||
|
||||
type Template1SectionScreenProps = {
|
||||
title: string;
|
||||
chips: string[];
|
||||
cards: Template1MenuItem[];
|
||||
onItemSelect: (item: Template1MenuItem) => void;
|
||||
};
|
||||
|
||||
function Template1HomeScreen({ items, onItemSelect }: Template1HomeScreenProps) {
|
||||
return (
|
||||
<>
|
||||
<header className="template1-app__section-head">
|
||||
<h2>콘텐츠 목록</h2>
|
||||
</header>
|
||||
<div className="template1-app__home-list" aria-label="콘텐츠 목록">
|
||||
{items.map((item) => (
|
||||
<button
|
||||
type="button"
|
||||
key={item.title}
|
||||
className="template1-app__home-item"
|
||||
onClick={() => onItemSelect({ ...item, screen: item.screen, actionType: 'navigate' })}
|
||||
>
|
||||
<span className="template1-app__card-icon">{item.icon}</span>
|
||||
<div>
|
||||
<strong>{item.title}</strong>
|
||||
<span>{item.description}</span>
|
||||
<small>{item.chips.join(' · ')}</small>
|
||||
</div>
|
||||
<span className="template1-app__card-action">{item.actionLabel ?? '열기'}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Template1SectionScreen({ title, chips, cards, onItemSelect }: Template1SectionScreenProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="template1-app__chips-text">{chips.join(' · ')}</div>
|
||||
<div className="template1-app__cards" aria-label={title}>
|
||||
<div className="template1-app__title-row">
|
||||
<h2>{title}</h2>
|
||||
</div>
|
||||
{cards.map((item) => (
|
||||
<button
|
||||
type="button"
|
||||
key={item.title}
|
||||
className="template1-app__card"
|
||||
onClick={() => onItemSelect(item)}
|
||||
>
|
||||
<span className="template1-app__card-icon">{item.icon}</span>
|
||||
<div>
|
||||
<strong>{item.title}</strong>
|
||||
<span>{item.description}</span>
|
||||
</div>
|
||||
<span className="template1-app__card-action">{item.actionLabel ?? '열기'}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Template1SettingsDropdown({
|
||||
onItemSelect,
|
||||
}: {
|
||||
onItemSelect: (item: Template1MenuItem) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="template1-app__settings-dropdown" role="menu" aria-label="설정 메뉴">
|
||||
<div className="template1-app__settings-list">
|
||||
{settingsItems.map((item) => (
|
||||
<button
|
||||
type="button"
|
||||
key={item.title}
|
||||
className={`template1-app__settings-item${item.actionType === 'exit' ? ' template1-app__settings-item--danger' : ''}`}
|
||||
onClick={() => onItemSelect(item)}
|
||||
>
|
||||
<span
|
||||
className={`template1-app__card-icon template1-app__settings-item-icon${item.actionType === 'exit' ? ' template1-app__settings-item-icon--danger' : ''}`}
|
||||
>
|
||||
{item.icon}
|
||||
</span>
|
||||
<div className="template1-app__settings-item-content">
|
||||
<strong>{item.title}</strong>
|
||||
<span
|
||||
className={item.actionType === 'exit' ? 'template1-app__settings-item-description--muted' : undefined}
|
||||
>
|
||||
{item.description}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Template1PlayAppView({ onBack, launchContext = 'direct' }: Template1PlayAppViewProps) {
|
||||
const [activeSection, setActiveSection] = useState<Template1SectionId>('home');
|
||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||
void launchContext;
|
||||
|
||||
const activeSectionItem = useMemo(
|
||||
() => TEMPLATE1_SECTIONS.find((item) => item.id === activeSection) ?? TEMPLATE1_SECTIONS[0],
|
||||
[activeSection],
|
||||
);
|
||||
|
||||
const menuItems = TEMPLATE1_MENUS[activeSection];
|
||||
|
||||
const homeMenuItems = useMemo(
|
||||
() =>
|
||||
TEMPLATE1_SECTIONS.filter((item) => item.id !== 'home')
|
||||
.map((item) => ({
|
||||
title: item.label,
|
||||
description: item.description,
|
||||
icon:
|
||||
item.id === 'projects' ? <ThunderboltOutlined /> :
|
||||
item.id === 'resources' ? <AppstoreAddOutlined /> :
|
||||
<BellOutlined />,
|
||||
screen: item.id,
|
||||
actionLabel: '열기',
|
||||
chips: item.chips,
|
||||
actionType: 'navigate',
|
||||
})),
|
||||
[],
|
||||
);
|
||||
|
||||
const goSection = (section: Template1SectionId) => {
|
||||
setActiveSection(section);
|
||||
setIsSettingsOpen(false);
|
||||
};
|
||||
|
||||
const openItem = (item: Template1MenuItem) => {
|
||||
if (item.actionType === 'exit') {
|
||||
onBack();
|
||||
setIsSettingsOpen(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.settingAction === 'reset-state') {
|
||||
goSection('home');
|
||||
setIsSettingsOpen(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.actionType === 'local') {
|
||||
setIsSettingsOpen(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.screen) {
|
||||
goSection(item.screen);
|
||||
}
|
||||
};
|
||||
|
||||
const handleHomeIconClick = () => {
|
||||
goSection('home');
|
||||
};
|
||||
|
||||
const renderActiveScreen = () => {
|
||||
if (activeSection === 'home') {
|
||||
return <Template1HomeScreen items={homeMenuItems} onItemSelect={openItem} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Template1SectionScreen
|
||||
title={activeSectionItem.label}
|
||||
chips={activeSectionItem.chips}
|
||||
cards={menuItems}
|
||||
onItemSelect={openItem}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="template1-app">
|
||||
<header className="template1-app__topbar">
|
||||
<div className="template1-app__brand">
|
||||
<button
|
||||
type="button"
|
||||
className="template1-app__badge"
|
||||
aria-label="Template1 홈으로 이동"
|
||||
onClick={handleHomeIconClick}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
handleHomeIconClick();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AppstoreOutlined />
|
||||
</button>
|
||||
<div>
|
||||
<h1>Template1 앱 레이아웃</h1>
|
||||
<p>다음 앱에 바로 적용 가능한 기본 배치 예시</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="template1-app__topbar-action"
|
||||
aria-label="Template1 설정 열기"
|
||||
aria-expanded={isSettingsOpen}
|
||||
onClick={() => setIsSettingsOpen((value) => !value)}
|
||||
title="설정"
|
||||
>
|
||||
<SettingOutlined />
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{isSettingsOpen ? (
|
||||
<Template1SettingsDropdown onItemSelect={openItem} />
|
||||
) : null}
|
||||
|
||||
<main className="template1-app__main">
|
||||
<div className="template1-app__scroll-area">
|
||||
<div key={`template1-section-${activeSection}-${isSettingsOpen ? 'settings' : 'screen'}`} className="template1-app__screen-transition">
|
||||
{renderActiveScreen()}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user