Spaces:
Runtime error
Runtime error
| import { create } from 'zustand' | |
| import type { StatementProblem, StatementResult } from '../types' | |
| import { judgeStatement } from '../lib/judge' | |
| import { fetchStatementProblems } from '../lib/supabase' | |
| import { useAuth } from './authStore' | |
| export type StatementPhase = 'select' | 'loading' | 'playing' | 'result' | |
| // ゲストモード用サンプル問題(貸借対照表の作成) | |
| const SAMPLE_STATEMENT: StatementProblem = { | |
| id: 'sample-statement-1', | |
| section: 'statement', | |
| topic: 'fixed_asset', | |
| event_text: | |
| '下記の決算整理前残高試算表にもとづき、決算整理を行って貸借対照表を完成させなさい。①貸倒引当金:売掛金の2%設定(差額補充法)。②減価償却:建物30年・備品8年(定額法・残存価額¥0)。③未払利息:借入金¥1,000,000の2か月分(年利3.6%・月割)。④消費税精算:仮受消費税-仮払消費税=未払消費税。⑤法人税等:¥100,000。', | |
| documents: [ | |
| { | |
| type: 'table', | |
| title: '決算整理前残高試算表(関連部分)', | |
| headers: ['勘定科目(借方)', '金額', '勘定科目(貸方)', '金額'], | |
| rows: [ | |
| ['売掛金', '450,000', '貸倒引当金', '4,000'], | |
| ['建物', '2,400,000', '建物減価償却累計額', '1,040,000'], | |
| ['備品', '700,000', '備品減価償却累計額', '262,500'], | |
| ['仮払消費税', '88,000', '借入金(年利3.6%・残2か月)', '1,000,000'], | |
| ['', '', '買掛金', '330,000'], | |
| ['', '', '仮受消費税', '160,000'], | |
| ], | |
| }, | |
| ], | |
| bs_rows: [ | |
| { | |
| left: { label: { type: 'given_label', value: '売掛金' }, | |
| amount: { type: 'blank_amount', answer: 450000 } }, | |
| right: { label: { type: 'given_label', value: '買掛金' }, | |
| amount: { type: 'given_amount', value: 330000 } }, | |
| }, | |
| { | |
| left: { label: { type: 'given_label', value: '貸倒引当金' }, | |
| amount: { type: 'blank_contra', answer: 9000, answer2: 441000 } }, | |
| right: { label: { type: 'given_label', value: '未払費用' }, | |
| amount: { type: 'blank_amount', answer: 6000 } }, | |
| }, | |
| { | |
| left: { label: { type: 'given_label', value: '建物' }, | |
| amount: { type: 'blank_amount', answer: 2400000 } }, | |
| right: { label: { type: 'blank_account', answer: '未払', suffix: '消費税' }, | |
| amount: { type: 'blank_amount', answer: 72000 } }, | |
| }, | |
| { | |
| left: { label: { type: 'given_label', value: '建物減価償却累計額' }, | |
| amount: { type: 'blank_contra', answer: 1120000, answer2: 1280000 } }, | |
| right: { label: { type: 'given_label', value: '未払法人税等' }, | |
| amount: { type: 'blank_amount', answer: 100000 } }, | |
| }, | |
| { | |
| left: { label: { type: 'given_label', value: '備品' }, | |
| amount: { type: 'blank_amount', answer: 700000 } }, | |
| right: { label: { type: 'given_label', value: null }, | |
| amount: { type: 'given_amount', value: null } }, | |
| }, | |
| { | |
| left: { label: { type: 'given_label', value: '備品減価償却累計額' }, | |
| amount: { type: 'blank_contra', answer: 350000, answer2: 350000 } }, | |
| right: { label: { type: 'given_label', value: null }, | |
| amount: { type: 'given_amount', value: null } }, | |
| }, | |
| ], | |
| hint: '①9,000(450,000×2%)②建物80,000(÷30)・備品87,500(÷8)③6,000(×3.6%×2/12)④72,000(160,000-88,000)', | |
| explanation: | |
| '貸倒引当金9,000(追加5,000)。建物累計1,120,000(+80,000)、備品累計350,000(+87,500)。未払費用6,000。未払消費税72,000。未払法人税等100,000。', | |
| } | |
| interface StatementState { | |
| phase: StatementPhase | |
| problem: StatementProblem | null | |
| inputs: Record<string, string | number> | |
| result: StatementResult | null | |
| error: string | null | |
| start: () => Promise<void> | |
| setInput: (key: string, value: string | number) => void | |
| submit: () => void | |
| retry: () => void | |
| backToSelect: () => void | |
| } | |
| export const useStatement = create<StatementState>((set, get) => ({ | |
| phase: 'select', | |
| problem: null, | |
| inputs: {}, | |
| result: null, | |
| error: null, | |
| start: async () => { | |
| set({ phase: 'loading', error: null }) | |
| const isGuest = useAuth.getState().authStatus === 'guest' | |
| if (isGuest) { | |
| set({ phase: 'playing', problem: SAMPLE_STATEMENT, inputs: {}, result: null }) | |
| return | |
| } | |
| try { | |
| const problems = await fetchStatementProblems() | |
| if (problems.length === 0) { | |
| set({ phase: 'select', error: '財務諸表問題がまだありません。管理者に問い合わせください。' }) | |
| return | |
| } | |
| set({ phase: 'playing', problem: problems[0], inputs: {}, result: null }) | |
| } catch (e) { | |
| set({ | |
| phase: 'select', | |
| error: e instanceof Error ? e.message : '問題の取得に失敗しました。', | |
| }) | |
| } | |
| }, | |
| setInput: (key, value) => { | |
| set((s) => ({ inputs: { ...s.inputs, [key]: value } })) | |
| }, | |
| submit: () => { | |
| const { problem, inputs } = get() | |
| if (!problem) return | |
| const result = judgeStatement(problem, inputs) | |
| set({ phase: 'result', result }) | |
| }, | |
| retry: () => { | |
| set({ inputs: {}, result: null, phase: 'playing' }) | |
| }, | |
| backToSelect: () => { | |
| set({ phase: 'select', problem: null, inputs: {}, result: null, error: null }) | |
| }, | |
| })) | |