feat(V2-6/V2-7/V2-8): Mobile MVP + Teams + Billing 스캐폴딩

V2-6 — Mobile (Expo) MVP
- apps/mobile/ 신규, npm workspace에서 제외 (Expo deps 부담 회피)
- Expo SDK 51 + Expo Router + AsyncStorage Supabase 클라이언트
- 화면: index/login/(tabs)/{meetings,record,profile}
- expo-av로 녹음 → Edge Function stt-proxy 호출
- expo-web-browser + expo-linking으로 OAuth 콜백 처리
- README에 setup/EAS build 가이드
- 루트 package.json workspaces를 명시 나열로 변경 (apps/mobile 제외)

V2-7 — 팀 기능 (Web)
- apps/web/src/app/teams/page.tsx — 가입한 팀 카드 그리드
- apps/web/src/app/teams/[id]/page.tsx — 멤버 + 공유 회의
- components/teams/create-team-form.tsx — 팀 생성 + owner 자동 team_members
- components/teams/invite-member-form.tsx — user_id 직접 초대 (V2-7b에서 invite flow)
- Sidebar에 Teams/Billing 메뉴 + 아이콘
- ko.json에 nav.teams/nav.billing 키 추가
- V2-2 teams/team_members RLS 활용

V2-8 — 결제 스캐폴딩
- apps/web/src/app/billing/page.tsx — Free/Pro/Team 가격표 + 현재 구독
- components/billing/checkout-button.tsx — Edge Function 호출 후 redirect
- server/supabase/functions/stripe-checkout/index.ts:
  - JWT 인증 -> 기존 customer 조회/생성 -> Checkout Session 생성
  - subscriptions 테이블에 customer_id upsert
- server/supabase/functions/stripe-webhook/index.ts:
  - checkout.session.completed -> subscriptions tier=pro/team active
  - customer.subscription.updated/created -> status/period 업데이트
  - customer.subscription.deleted -> tier=free, status=canceled
  - signature 검증은 placeholder (V2-8b에서 정식)
- server/supabase/config.toml에 두 함수 등록 (webhook verify_jwt=false)

검증:
- desktop typecheck OK (회귀 없음)
- web typecheck OK
- web next build OK (11 라우트)
- mobile은 별도 install 필요 (workspace 제외)

memory/project_status.md 갱신 — V2 마스터 플랜 전 페이즈 로컬 완료
This commit is contained in:
yunchan8804 2026-04-09 16:39:54 +09:00
parent 5c0f4a2b98
commit 61a96b3e9b
136 changed files with 2641 additions and 251 deletions

View file

@ -0,0 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[177],{255:(e,t,r)=>{Promise.resolve().then(r.bind(r,1598)),Promise.resolve().then(r.bind(r,1732)),Promise.resolve().then(r.bind(r,7455))},1598:(e,t,r)=>{"use strict";r.d(t,{A:()=>c,AuthProvider:()=>s});var o=r(4568),n=r(7620),l=r(1037);let a=(0,n.createContext)({session:null,user:null,loading:!0});function s(e){var t;let{children:r}=e,[s,c]=(0,n.useState)(null),[i,u]=(0,n.useState)(!0);return(0,n.useEffect)(()=>{let e=(0,l.c)();e.auth.getSession().then(e=>{let{data:t}=e;c(t.session)}).finally(()=>{u(!1)});let{data:{subscription:t}}=e.auth.onAuthStateChange((e,t)=>{c(t)});return()=>{t.unsubscribe()}},[]),(0,o.jsx)(a.Provider,{value:{session:s,user:null!=(t=null==s?void 0:s.user)?t:null,loading:i},children:r})}function c(){return(0,n.useContext)(a)}},1732:(e,t,r)=>{"use strict";r.d(t,{I18nProvider:()=>s});var o=r(4568),n=r(7740);let l="d3ro.voice.locale",a={load:()=>{try{return window.localStorage.getItem(l)}catch(e){return null}},save:e=>{try{window.localStorage.setItem(l,e)}catch(e){}}};function s(e){let{children:t}=e;return(0,o.jsx)(n.CY,{storage:a,children:t})}},7455:(e,t,r)=>{"use strict";r.d(t,{ThemeProvider:()=>f});var o=r(4568),n=r(7620),l=r(9746),a=r(6480),s=r(5120);let c="function"==typeof(0,a.Dp)({}),i=function(e){var t,r;let o=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n={};o&&e.colorSchemes&&"function"==typeof e.getColorSchemeSelector&&Object.entries(e.colorSchemes).forEach(t=>{var r,o;let[l,a]=t,s=e.getColorSchemeSelector(l);s.startsWith("@")?n[s]={":root":{colorScheme:null==(r=a.palette)?void 0:r.mode}}:n[s.replace(/\s*&/,"")]={colorScheme:null==(o=a.palette)?void 0:o.mode}});let l={html:{WebkitFontSmoothing:"antialiased",MozOsxFontSmoothing:"grayscale",boxSizing:"border-box",WebkitTextSizeAdjust:"100%",...o&&!e.vars&&{colorScheme:e.palette.mode}},"*, *::before, *::after":{boxSizing:"inherit"},"strong, b":{fontWeight:e.typography.fontWeightBold},body:{margin:0,...{color:(e.vars||e).palette.text.primary,...e.typography.body1,backgroundColor:(e.vars||e).palette.background.default,"@media print":{backgroundColor:(e.vars||e).palette.common.white}},"&::backdrop":{backgroundColor:(e.vars||e).palette.background.default}},...n},a=null==(r=e.components)||null==(t=r.MuiCssBaseline)?void 0:t.styleOverrides;return a&&(l=[l,a]),l},u="mui-ecs",h=(0,a.Dp)(c?e=>{let{theme:t,enableColorScheme:r}=e;return i(t,r)}:e=>{let{theme:t}=e;return(e=>{let t=i(e,!1),r=Array.isArray(t)?t[0]:t;return!e.vars&&r&&(r.html[":root:has(".concat(u,")")]={colorScheme:e.palette.mode}),e.colorSchemes&&Object.entries(e.colorSchemes).forEach(t=>{var o,n;let[l,a]=t,s=e.getColorSchemeSelector(l);s.startsWith("@")?r[s]={[":root:not(:has(.".concat(u,"))")]:{colorScheme:null==(o=a.palette)?void 0:o.mode}}:r[s.replace(/\s*&/,"")]={["&:not(:has(.".concat(u,"))")]:{colorScheme:null==(n=a.palette)?void 0:n.mode}}}),t})(t)}),d=function(e){let{children:t,enableColorScheme:r=!1}=(0,s.b)({props:e,name:"MuiCssBaseline"});return(0,o.jsxs)(n.Fragment,{children:[c&&(0,o.jsx)(h,{enableColorScheme:r}),!c&&!r&&(0,o.jsx)("span",{className:u,style:{display:"none"}}),t]})};var m=r(1828),v=r(3459),p=r(7541);function y(e){let{options:t,CacheProvider:r=v.C,children:l}=e,[a]=n.useState(()=>{var e;let r=(0,m.A)({...t,key:null!=(e=null==t?void 0:t.key)?e:"mui"});r.compat=!0;let o=r.insert,n=[];return r.insert=function(){for(var e=arguments.length,l=Array(e),a=0;a<e;a++)l[a]=arguments[a];(null==t?void 0:t.enableCssLayer)&&!l[1].styles.match(/^@layer\s+[^{]*$/)&&(l[1].styles="@layer mui {".concat(l[1].styles,"}"));let[s,c]=l;return void 0===r.inserted[c.name]&&n.push({name:c.name,isGlobal:!s}),o(...l)},{cache:r,flush:()=>{let e=n;return n=[],e}}});return(0,p.useServerInsertedHTML)(()=>{let e=a.flush();if(0===e.length)return null;let r="",l=a.cache.key,s=[];return e.forEach(e=>{let{name:t,isGlobal:o}=e,n=a.cache.inserted[t];"string"==typeof n&&(o?s.push({name:t,style:n}):(r+=n,l+=" ".concat(t)))}),(0,o.jsxs)(n.Fragment,{children:[s.map(e=>{let{name:r,style:n}=e;return(0,o.jsx)("style",{nonce:null==t?void 0:t.nonce,"data-emotion":"".concat(a.cache.key,"-global ").concat(r),dangerouslySetInnerHTML:{__html:n}},r)}),r&&(0,o.jsx)("style",{nonce:null==t?void 0:t.nonce,"data-emotion":l,dangerouslySetInnerHTML:{__html:r}})]})}),(0,o.jsx)(r,{value:a.cache,children:l})}var g=r(3791);function f(e){let{children:t}=e,r=(0,n.useMemo)(()=>(0,g.O4)("dark",!0),[]);return(0,o.jsx)(y,{options:{key:"d3ro-mui",enableCssLayer:!0},children:(0,o.jsxs)(l.default,{theme:r,children:[(0,o.jsx)(d,{}),t]})})}}},e=>{e.O(0,[385,77,988,746,791,803,587,18,358],()=>e(e.s=255)),_N_E=e.O()}]);