d3ro-voice/apps/web/.next/static/chunks/app/login/page-6472292893f1e497.js
yunchan8804 d0c33ca259 feat(V2-3): Web App MVP — Next.js 15 + @d3ro/api-client
packages/api-client (@d3ro/api-client) 신규:
- types.ts: 12개 테이블 Row/Insert/Update 타입 + Database 형식
- client.ts: createD3roSupabaseClient 팩토리 (placeholder fallback)
- auth.ts: signInWithOAuth/signOut/getSession/onAuthStateChange
- meetings.ts: 회의 + 메모 + 문서 + 전사 CRUD
- history.ts: 음성 입력 이력 CRUD
- usage.ts: 일일 쿼터 + 구독 조회
- 루트 barrel은 types만 re-export, 함수는 subpath import 전용

apps/web (@d3ro/web) 신규 — Next.js 15 App Router:
- transpilePackages: @d3ro/core, ui, i18n, api-client
- ThemeProvider (MUI + AppRouterCacheProvider)
- I18nProvider (localStorage 어댑터)
- AuthProvider (Supabase session Context)
- 라우트 9개:
  - / (auth 상태 기반 리다이렉트)
  - /login (Google/GitHub OAuth, 미설정 경고)
  - /auth/callback (code -> session 교환)
  - /dashboard (요약 카드 + 최근 회의)
  - /meetings (카드 그리드 리스트)
  - /meetings/[id] (transcripts/memos/documents)
  - /record (getUserMedia + MediaRecorder + stt-proxy)
- Sidebar, 인증 가드, 9바 웨이브폼, 레벨 미터

packages/ui 확장:
- MetalCard가 BoxProps 상속 (sx/onClick 등 전달)
- theme.ts에 typoSx(key) 헬퍼 추가 (d3roTypo -> MUI sx 변환)
- d3roPalette.tag.blue 추가 (M5 정리 포함)
- DS 컴포넌트 9개에 'use client' directive
- MetalDial 미사용 import 제거

packages/i18n 확장:
- ko.json에 7개 새 키 (nav.meetings/record/logout, login.*)

설계 결정:
- Database 제네릭 현재는 기본 타입 (V2-4에서 supabase gen types로 자동화)
- api-client barrel은 타입만 노출해서 컴파일 전파 차단
- DS 컴포넌트 client 경계 명시
- env 없어도 Next.js 빌드 성공 (placeholder URL/key)

검증:
- web typecheck OK
- desktop typecheck OK (회귀 없음)
- web next build OK (9 라우트 정적/동적 생성)
- desktop build OK (회귀 없음)
2026-04-09 02:52:31 +09:00

1 line
No EOL
4 KiB
JavaScript

(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[520],{1009:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var l=n(4568),s=n(7541),a=n(7620),i=n(1284),r=n(5754),u=n(1181),o=n(2622),c=n(8703);let d=(0,c.A)((0,l.jsx)("path",{d:"M12.545,10.239v3.821h5.445c-0.712,2.315-2.647,3.972-5.445,3.972c-3.332,0-6.033-2.701-6.033-6.032s2.701-6.032,6.033-6.032c1.498,0,2.866,0.549,3.921,1.453l2.814-2.814C17.503,2.988,15.139,2,12.545,2C7.021,2,2.543,6.477,2.543,12s4.478,10,10.002,10c8.396,0,10.249-7.85,9.426-11.748L12.545,10.239z"}),"Google"),h=(0,c.A)((0,l.jsx)("path",{d:"M12 1.27a11 11 0 00-3.48 21.46c.55.09.73-.28.73-.55v-1.84c-3.03.64-3.67-1.46-3.67-1.46-.55-1.29-1.28-1.65-1.28-1.65-.92-.65.1-.65.1-.65 1.1 0 1.73 1.1 1.73 1.1.92 1.65 2.57 1.2 3.21.92a2 2 0 01.64-1.47c-2.47-.27-5.04-1.19-5.04-5.5 0-1.1.46-2.1 1.2-2.84a3.76 3.76 0 010-2.93s.91-.28 3.11 1.1c1.8-.49 3.7-.49 5.5 0 2.1-1.38 3.02-1.1 3.02-1.1a3.76 3.76 0 010 2.93c.83.74 1.2 1.74 1.2 2.94 0 4.21-2.57 5.13-5.04 5.4.45.37.82.92.82 2.02v3.03c0 .27.1.64.73.55A11 11 0 0012 1.27"}),"GitHub");var x=n(1441),g=n(3791),v=n(7740),f=n(1037),b=n(1598);function p(){var e,t,n,c;let p=(0,s.useRouter)(),{user:j,loading:A}=(0,b.A)(),{t:C}=(0,v.s9)(),[k,m]=(0,a.useState)(null),[w,S]=(0,a.useState)(!1),y=(0,f.$)();async function E(e){if(!y)return void m("Supabase 환경변수가 설정되지 않았습니다. apps/web/env.example.txt를 참고하세요.");m(null),S(!0);try{let t=(0,f.c)(),n="".concat(window.location.origin,"/auth/callback"),{error:l}=await t.auth.signInWithOAuth({provider:e,options:{redirectTo:n}});l&&(m(l.message),S(!1))}catch(e){m(e instanceof Error?e.message:"Unknown error"),S(!1)}}return(0,a.useEffect)(()=>{!A&&j&&p.replace("/dashboard")},[A,j,p]),(0,l.jsx)(i.default,{sx:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",bgcolor:g.sM.bg.app,p:4},children:(0,l.jsx)(x.Uu,{sx:{maxWidth:420,width:"100%",p:4},children:(0,l.jsxs)(r.default,{spacing:3,children:[(0,l.jsxs)(i.default,{sx:{textAlign:"center"},children:[(0,l.jsx)(x.Rk,{variant:"title",children:"D3RO VOICE"}),(0,l.jsx)(i.default,{sx:{mt:1,...(0,g.kR)("label"),color:g.sM.text.secondary},children:null!=(e=C("login.subtitle"))?e:"AI 음성 어시스턴트"})]}),!y&&(0,l.jsx)(u.A,{severity:"warning",variant:"outlined",children:"Supabase 환경변수가 설정되지 않았습니다. 로그인은 env 설정 후 가능합니다."}),k&&(0,l.jsx)(u.A,{severity:"error",variant:"outlined",children:k}),(0,l.jsxs)(r.default,{spacing:2,children:[(0,l.jsx)(o.A,{fullWidth:!0,variant:"contained",size:"large",startIcon:(0,l.jsx)(d,{}),disabled:w||!y,onClick:()=>void E("google"),children:null!=(t=C("login.google"))?t:"Google로 계속하기"}),(0,l.jsx)(o.A,{fullWidth:!0,variant:"outlined",size:"large",startIcon:(0,l.jsx)(h,{}),disabled:w||!y,onClick:()=>void E("github"),children:null!=(n=C("login.github"))?n:"GitHub로 계속하기"})]}),(0,l.jsx)(i.default,{sx:{textAlign:"center",color:g.sM.text.label,fontSize:12},children:null!=(c=C("login.terms"))?c:"계속 진행하면 이용약관 및 개인정보 처리방침에 동의합니다."})]})})})}},1441:(e,t,n)=>{"use strict";n.d(t,{Rk:()=>s.PhosphorText,Uu:()=>l.MetalCard}),n(4070),n(9006),n(5281),n(6652);var l=n(9502),s=n(215);n(2517),n(1459),n(984)},1598:(e,t,n)=>{"use strict";n.d(t,{A:()=>u,AuthProvider:()=>r});var l=n(4568),s=n(7620),a=n(1037);let i=(0,s.createContext)({session:null,user:null,loading:!0});function r(e){var t;let{children:n}=e,[r,u]=(0,s.useState)(null),[o,c]=(0,s.useState)(!0);return(0,s.useEffect)(()=>{let e=(0,a.c)();e.auth.getSession().then(e=>{let{data:t}=e;u(t.session)}).finally(()=>{c(!1)});let{data:{subscription:t}}=e.auth.onAuthStateChange((e,t)=>{u(t)});return()=>{t.unsubscribe()}},[]),(0,l.jsx)(i.Provider,{value:{session:r,user:null!=(t=null==r?void 0:r.user)?t:null,loading:o},children:n})}function u(){return(0,s.useContext)(i)}},8119:(e,t,n)=>{Promise.resolve().then(n.bind(n,1009))}},e=>{e.O(0,[385,77,988,189,140,374,791,184,910,587,18,358],()=>e(e.s=8119)),_N_E=e.O()}]);