Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4b2ec57
[feat] 토론 기록 초안 저장소와 크롬 음성 인식 경계 추가
coli-geonwoo Oct 22, 2026
c782a21
[feat] 토론 기록 저장·조회 API와 Query·Mutation 훅 추가
coli-geonwoo Oct 22, 2026
6130cc9
[feat] 토론 기록 화면 번역 문구 추가
coli-geonwoo Oct 22, 2026
9e2d77a
[feat] 타이머 화면 토론 음성 기록 켜기·끄기 추가
coli-geonwoo Oct 22, 2026
e6fd995
[feat] 토론 종료 화면에서 기록 초안 전송 추가
coli-geonwoo Oct 22, 2026
c38d958
[feat] 토론 속기록·분석 보고서 화면 추가
coli-geonwoo Oct 22, 2026
7767938
[style] 토론 기록 화면 Tailwind 4 클래스 정렬
coli-geonwoo Oct 22, 2026
48bcb8b
[feat] 토론 기록 초안에 STT 모드와 전사 대기 구간 추가
coli-geonwoo Oct 22, 2026
40e3027
[feat] 발언 구간 오디오 녹음 브라우저 경계 추가
coli-geonwoo Oct 22, 2026
231d1da
[feat] Soniox 발언 구간 전사 API와 토론 기록 STT 모드 계약 추가
coli-geonwoo Oct 22, 2026
b101a76
[feat] Soniox 모드 발언 구간 녹음·배치 전사 훅 추가
coli-geonwoo Oct 22, 2026
9868bcd
[feat] 토론 기록 STT 모드 선택 훅 추가
coli-geonwoo Oct 22, 2026
4d1fc00
[feat] 토론 기록 동의 모달에서 RPC·Soniox 모드 선택
coli-geonwoo Oct 22, 2026
04cafce
[feat] 종료 화면에서 Soniox 전사가 끝난 뒤 토론 기록 전송
coli-geonwoo Oct 22, 2026
9b0ee6a
[feat] 속기록 화면에 음성 인식 방식 표시
coli-geonwoo Oct 22, 2026
8deff4d
[docs] 파일 업로드 요청 옵션과 토론 기록 STT 모드 초안 계약 문서화
coli-geonwoo Oct 22, 2026
77b2294
[fix] Soniox 구간 녹음 마무리 전 종료 화면 이동 시 마지막 구간 누락 수정
coli-geonwoo Oct 22, 2026
5939a29
[feat] 속기록에서 같은 팀이 이어서 말한 문장을 하나의 발언으로 합쳐 표시
coli-geonwoo Oct 22, 2026
4f606c9
[feat] 속기록 보정 중 상태 표시와 보정된 속기록 조회
coli-geonwoo Oct 22, 2026
c8bbca9
[style] 토론 기록 초안 테스트 포맷 정리
coli-geonwoo Oct 22, 2026
34b947f
[fix] Soniox 기록 중 마이크 스트림이 끊기면 다시 요청
coli-geonwoo Oct 22, 2026
75117ca
[fix] 데모 이식 중 되돌려진 MenuCard를 develop 버전으로 복구
coli-geonwoo Oct 22, 2026
09443ac
[style] 토론 기록 테스트 이름 한국어로 통일 및 음성 인식 결과 반복문 정리
coli-geonwoo Oct 22, 2026
842a13c
[feat] 토론 기록 목록 조회 API와 Query 훅 추가
coli-geonwoo Oct 23, 2026
dc2bed2
[feat] 타이머 화면 녹음 컨트롤 정리
coli-geonwoo Oct 23, 2026
540e793
[feat] 속기록 보정·보고서 발행이 끝날 때까지 진행 단계 화면 표시
coli-geonwoo Oct 23, 2026
8bb670d
[feat] 대시보드에 토론 기록 탭 추가
coli-geonwoo Oct 23, 2026
1f0aab5
[fix] 진행 단계 화면의 토론 기록 링크가 랜딩 페이지로 가던 문제 수정
coli-geonwoo Oct 23, 2026
bf62b35
[feat] 토론 기록 탭에 새 기록 N 표시
coli-geonwoo Oct 23, 2026
e76ed90
[design] 토론 기록 카드를 차콜 블랙으로 변경
coli-geonwoo Oct 23, 2026
6a736f2
[design] 속기록·진행 단계 화면 머리를 토론 기록 카드와 같은 차콜 카드로 통일
coli-geonwoo Oct 23, 2026
e9573bd
[design] 분석 보고서의 테마 밖 색을 테마 색으로 통일
coli-geonwoo Oct 23, 2026
3a76206
[feat] 토론 기록 탭에 New 태그를 항상 표시
coli-geonwoo Oct 23, 2026
9570077
[fix] 녹음 훅 안정성 보완 (CodeRabbit 리뷰 반영)
coli-geonwoo Oct 23, 2026
e61cf16
[fix] 분석 보고서 목록 key가 AI 생성 문장과 겹치지 않게 수정 (CodeRabbit 리뷰 반영)
coli-geonwoo Oct 23, 2026
1dc56e9
[fix] 대시보드 탭마다 tabpanel을 연결 (CodeRabbit 리뷰 반영)
coli-geonwoo Oct 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions docs/architecture/data-access.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ AxiosResponse 전체 대신 `response.data`로 반환함. 요청·응답 계약
`src/apis/requests/`, `src/apis/responses/`에 배치함. 모든 요청 body를 별도 타입
파일에 두는 규칙은 없음.

파일 업로드처럼 기본 JSON 헤더·5초 제한 시간과 다른 요청은 다섯째 인자
`options`의 `headers`·`timeout`으로 덮어씀. FormData는 `Content-Type`을
`multipart/form-data`로 지정해야 Axios가 JSON으로 바꾸지 않고 boundary를 붙임.
`src/apis/apis/stt.ts`의 `postSonioxTranscription()`이 예시임. jsdom의
FormData·Blob 본문은 MSW가 끝까지 읽지 못하므로 이 API의 직접 MSW 테스트는
`// @vitest-environment node`에서 실행함.

`src/apis/apis/live.ts`의 `getChairmanToken()`은 `ApiUrl.live`에 인코딩된 table
ID를 붙여 `request<GetChairmanTokenResponseType>('GET', endpoint, null, null)`을
호출한 뒤 `response.data`를 반환함. 화면이 Axios 설정이나 HTTP 응답 전체를 알
Expand Down Expand Up @@ -54,8 +61,11 @@ ID를 붙여 `request<GetChairmanTokenResponseType>('GET', endpoint, null, null)
`src/repositories/DebateTableRepository.ts`가 토론 테이블 CRUD 계약과
`getRepository()` 선택을 소유함. 게스트 흐름은 `SessionDebateTableRepository`,
그 외는 `ApiDebateTableRepository`를 사용함. 두 구현은 같은 인터페이스를 따르는
인스턴스로 제공됨. 세션 저장의 브라우저 경계는 `src/util/sessionStorage.ts`가
소유함.
인스턴스로 제공됨. 게스트 테이블 세션 저장의 브라우저 경계는
`src/util/sessionStorage.ts`가 소유함. 타이머 화면에서 쌓아 종료 화면에서 보내는
토론 기록 초안은 `src/util/transcriptDraft.ts`가 테이블별 키로 소유함. 초안은
STT 모드(`RPC`·`SONIOX`)와 Soniox 전사를 기다리는 구간을 함께 저장하고, 종료
화면은 대기 구간이 끝난 뒤 초안을 보냄.

`src/mocks/handlers/global.ts`의 `allHandlers`가 브라우저 개발 모드와 Node
테스트의 공통 핸들러 집합임. `src/mocks/browser.ts`와 `src/mocks/server.ts`는
Expand Down
66 changes: 65 additions & 1 deletion public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,5 +317,69 @@
"다시 시도해보세요.": "Please try again.",
"링크 공유": "Share Link",
"링크 복사됨": "Link Copied",
"링크 복사 실패": "Copy Failed"
"링크 복사 실패": "Copy Failed",
"분석 보고서를 만들지 못했어요": "We couldn't create the analysis report",
"{{count}}문장": "{{count}} sentences",
"{{label}}: 속기록에서 보기": "{{label}}: view in transcript",
"{{side}} 우세": "{{side}} ahead",
"근거 없음": "No evidence",
"논리·사례": "Logic / example",
"출처 명시": "Source cited",
"출처 불명확": "Unclear source",
"기록 보기 방식": "Record view",
"다시 보내기": "Send again",
"동의하고 기록하기": "Agree and record",
"마이크 권한을 허용해야 토론을 기록할 수 있어요": "Allow microphone access to record the debate",
"보기": "View",
"분석 보고서": "Analysis report",
"속기록": "Transcript",
"속기록·분석 보고서 보기": "View transcript and report",
"속기록에서 {{time}} 발언 보기": "View the {{time}} remark in transcript",
"아쉬웠던 점": "To improve",
"좋았던 점": "Strengths",
"양측 주장과 근거": "Claims and evidence",
"충돌했던 쟁점": "Clashing issues",
"큰 역할을 했던 발언": "Key remarks",
"팀별 좋았던 점 · 아쉬웠던 점": "Team strengths and areas to improve",
"이번 토론이 자동으로 기록됐어요": "This debate was recorded automatically",
"정리된 주장이 없어요": "No claims found",
"주장 {{number}}": "Claim {{number}}",
"찬·반 화자가 구분된 속기록과 AI 분석 보고서를 볼 수 있어요": "See a speaker-separated transcript and an AI analysis report",
"참여자 모두에게 녹음 동의를 받아 주세요.": "Please get recording consent from every participant.",
"타이머를 시작하면 기록해요": "Records while the timer runs",
"토론 기록": "Debate record",
"토론 기록 ID가 올바르지 않습니다.": "The debate record ID is invalid.",
"토론 기록 끄기": "Turn off debate recording",
"토론 기록 켜기": "Turn on debate recording",
"토론 기록을 보내고 있어요": "Sending the debate record",
"토론 기록을 보내지 못했어요": "We couldn't send the debate record",
"토론 음성 기록에 동의하시나요?": "Do you agree to record the debate audio?",
"타이머가 실행되는 동안의 발언을 구글 음성 인식으로 텍스트로 바꿔요. 토론이 끝나면 속기록을 서버로 보내 AI가 분석 보고서를 만들어요.": "While the timer runs, speech is converted to text with Google speech recognition. When the debate ends, the transcript is sent to our server and AI writes an analysis report.",
"팽팽": "Even",
"실시간 인식 (크롬)": "Real-time (Chrome)",
"발언 후 일괄 인식 (Soniox)": "After each speech (Soniox)",
"음성 인식 방식": "Speech recognition mode",
"발언 구간이 끝날 때마다 녹음한 오디오를 서버로 보내 Soniox로 텍스트로 바꿔요. 토론이 끝나면 속기록을 서버로 보내 AI가 분석 보고서를 만들어요.": "Each time a speech segment ends, its recorded audio is sent to our server and converted to text with Soniox. When the debate ends, the transcript is sent to our server and AI writes an analysis report.",
"이 브라우저에서는 토론 기록을 사용할 수 없어요": "Debate recording is not available in this browser",
"녹음 중": "Recording",
"{{count}}개 구간 전사 중": "Transcribing {{count}} segment(s)",
"{{count}}개 구간 전사 실패": "{{count}} segment(s) failed to transcribe",
"발언을 텍스트로 바꾸고 있어요 ({{count}}개 구간 남음)": "Converting speech to text ({{count}} segment(s) left)",
"음성 인식: {{mode}}": "Speech recognition: {{mode}}",
"속기록 보정 중": "Polishing transcript",
"분석 보고서 발행 중": "Publishing analysis report",
"음성 인식 오류와 문장부호를 다듬고 있어요": "Fixing speech recognition errors and punctuation",
"쟁점과 핵심 발언을 정리하고 있어요": "Summarizing clashes and key moments",
"토론 기록을 만들고 있어요": "Creating the debate record",
"토론 기록 생성 진행률": "Debate record progress",
"화면을 닫아도 괜찮아요. 완료되면 대시보드의 토론 기록에서 볼 수 있어요.": "You can close this page. Once it's ready, find it under Debate records on the dashboard.",
"토론 기록으로 가기": "Go to debate records",
"시간표": "Timetables",
"대시보드": "Dashboard",
"생성 중": "Generating",
"삭제된 시간표": "Deleted timetable",
"주제를 알 수 없어요": "Unknown topic",
"아직 토론 기록이 없어요": "No debate records yet",
"타이머 화면에서 녹음을 켜고 토론을 진행해 보세요.": "Turn on recording on the timer screen and run a debate.",
"타이머로 진행한 토론이 끝날 때마다 속기록과 분석 보고서가 자동으로 쌓여요.": "Each debate you run with the timer adds a transcript and an analysis report here automatically."
}
66 changes: 65 additions & 1 deletion public/locales/ko/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,5 +317,69 @@
"다시 시도해보세요.": "다시 시도해보세요.",
"링크 공유": "링크 공유",
"링크 복사됨": "링크 복사됨",
"링크 복사 실패": "링크 복사 실패"
"링크 복사 실패": "링크 복사 실패",
"분석 보고서를 만들지 못했어요": "분석 보고서를 만들지 못했어요",
"{{count}}문장": "{{count}}문장",
"{{label}}: 속기록에서 보기": "{{label}}: 속기록에서 보기",
"{{side}} 우세": "{{side}} 우세",
"근거 없음": "근거 없음",
"논리·사례": "논리·사례",
"출처 명시": "출처 명시",
"출처 불명확": "출처 불명확",
"기록 보기 방식": "기록 보기 방식",
"다시 보내기": "다시 보내기",
"동의하고 기록하기": "동의하고 기록하기",
"마이크 권한을 허용해야 토론을 기록할 수 있어요": "마이크 권한을 허용해야 토론을 기록할 수 있어요",
"보기": "보기",
"분석 보고서": "분석 보고서",
"속기록": "속기록",
"속기록·분석 보고서 보기": "속기록·분석 보고서 보기",
"속기록에서 {{time}} 발언 보기": "속기록에서 {{time}} 발언 보기",
"아쉬웠던 점": "아쉬웠던 점",
"좋았던 점": "좋았던 점",
"양측 주장과 근거": "양측 주장과 근거",
"충돌했던 쟁점": "충돌했던 쟁점",
"큰 역할을 했던 발언": "큰 역할을 했던 발언",
"팀별 좋았던 점 · 아쉬웠던 점": "팀별 좋았던 점 · 아쉬웠던 점",
"이번 토론이 자동으로 기록됐어요": "이번 토론이 자동으로 기록됐어요",
"정리된 주장이 없어요": "정리된 주장이 없어요",
"주장 {{number}}": "주장 {{number}}",
"찬·반 화자가 구분된 속기록과 AI 분석 보고서를 볼 수 있어요": "찬·반 화자가 구분된 속기록과 AI 분석 보고서를 볼 수 있어요",
"참여자 모두에게 녹음 동의를 받아 주세요.": "참여자 모두에게 녹음 동의를 받아 주세요.",
"타이머를 시작하면 기록해요": "타이머를 시작하면 기록해요",
"토론 기록": "토론 기록",
"토론 기록 ID가 올바르지 않습니다.": "토론 기록 ID가 올바르지 않습니다.",
"토론 기록 끄기": "토론 기록 끄기",
"토론 기록 켜기": "토론 기록 켜기",
"토론 기록을 보내고 있어요": "토론 기록을 보내고 있어요",
"토론 기록을 보내지 못했어요": "토론 기록을 보내지 못했어요",
"토론 음성 기록에 동의하시나요?": "토론 음성 기록에 동의하시나요?",
"타이머가 실행되는 동안의 발언을 구글 음성 인식으로 텍스트로 바꿔요. 토론이 끝나면 속기록을 서버로 보내 AI가 분석 보고서를 만들어요.": "타이머가 실행되는 동안의 발언을 구글 음성 인식으로 텍스트로 바꿔요. 토론이 끝나면 속기록을 서버로 보내 AI가 분석 보고서를 만들어요.",
"팽팽": "팽팽",
"실시간 인식 (크롬)": "실시간 인식 (크롬)",
"발언 후 일괄 인식 (Soniox)": "발언 후 일괄 인식 (Soniox)",
"음성 인식 방식": "음성 인식 방식",
"발언 구간이 끝날 때마다 녹음한 오디오를 서버로 보내 Soniox로 텍스트로 바꿔요. 토론이 끝나면 속기록을 서버로 보내 AI가 분석 보고서를 만들어요.": "발언 구간이 끝날 때마다 녹음한 오디오를 서버로 보내 Soniox로 텍스트로 바꿔요. 토론이 끝나면 속기록을 서버로 보내 AI가 분석 보고서를 만들어요.",
"이 브라우저에서는 토론 기록을 사용할 수 없어요": "이 브라우저에서는 토론 기록을 사용할 수 없어요",
"녹음 중": "녹음 중",
"{{count}}개 구간 전사 중": "{{count}}개 구간 전사 중",
"{{count}}개 구간 전사 실패": "{{count}}개 구간 전사 실패",
"발언을 텍스트로 바꾸고 있어요 ({{count}}개 구간 남음)": "발언을 텍스트로 바꾸고 있어요 ({{count}}개 구간 남음)",
"음성 인식: {{mode}}": "음성 인식: {{mode}}",
"속기록 보정 중": "속기록 보정 중",
"분석 보고서 발행 중": "분석 보고서 발행 중",
"음성 인식 오류와 문장부호를 다듬고 있어요": "음성 인식 오류와 문장부호를 다듬고 있어요",
"쟁점과 핵심 발언을 정리하고 있어요": "쟁점과 핵심 발언을 정리하고 있어요",
"토론 기록을 만들고 있어요": "토론 기록을 만들고 있어요",
"토론 기록 생성 진행률": "토론 기록 생성 진행률",
"화면을 닫아도 괜찮아요. 완료되면 대시보드의 토론 기록에서 볼 수 있어요.": "화면을 닫아도 괜찮아요. 완료되면 대시보드의 토론 기록에서 볼 수 있어요.",
"토론 기록으로 가기": "토론 기록으로 가기",
"시간표": "시간표",
"대시보드": "대시보드",
"생성 중": "생성 중",
"삭제된 시간표": "삭제된 시간표",
"주제를 알 수 없어요": "주제를 알 수 없어요",
"아직 토론 기록이 없어요": "아직 토론 기록이 없어요",
"타이머 화면에서 녹음을 켜고 토론을 진행해 보세요.": "타이머 화면에서 녹음을 켜고 토론을 진행해 보세요.",
"타이머로 진행한 토론이 끝날 때마다 속기록과 분석 보고서가 자동으로 쌓여요.": "타이머로 진행한 토론이 끝날 때마다 속기록과 분석 보고서가 자동으로 쌓여요."
}
48 changes: 48 additions & 0 deletions src/apis/apis/stt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// jsdom의 FormData·Blob 본문은 MSW가 읽지 못해 끝나지 않으므로 Node 환경에서 검증한다.
// @vitest-environment node
import { http, HttpResponse } from 'msw';
import { server } from '../../mocks/server';
import axiosInstance from '../axiosInstance';
import { ApiUrl } from '../endpoints';
import { PostSonioxTranscriptionResponseType } from '../responses/stt';
import { postSonioxTranscription } from './stt';

describe('음성 전사 API', () => {
beforeEach(() => {
// Node 환경에는 요청 인터셉터가 읽는 localStorage와 상대 경로의 기준 주소가 없다.
vi.stubGlobal('localStorage', { getItem: () => null });
axiosInstance.defaults.baseURL = 'http://localhost';
});

afterEach(() => {
vi.unstubAllGlobals();
axiosInstance.defaults.baseURL = undefined;
});

it('발언 구간 오디오와 언어를 multipart로 보내 Soniox 전사 문장을 받는다', async () => {
const response: PostSonioxTranscriptionResponseType = {
utterances: [{ offsetMs: 300, text: '찬성 입론입니다.' }],
};
server.use(
http.post(`http://localhost${ApiUrl.stt}/soniox`, async ({ request }) => {
expect(request.headers.get('content-type')).toContain(
'multipart/form-data',
);
const form = await request.formData();
const audio = form.get('audio') as File;
expect(audio.name).toBe('segment.webm');
expect(audio.size).toBe(3);
expect(form.get('language')).toBe('ko-KR');
return HttpResponse.json(response);
}),
);

const data = await postSonioxTranscription(
new Blob([new Uint8Array([1, 2, 3])], { type: 'audio/webm' }),
'segment.webm',
'ko-KR',
);

expect(data).toEqual(response);
});
});
29 changes: 29 additions & 0 deletions src/apis/apis/stt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ApiUrl } from '../endpoints';
import { request } from '../primitives';
import { PostSonioxTranscriptionResponseType } from '../responses/stt';

// 서버가 Soniox 전사를 마칠 때까지(최대 3분) 기다린다.
const SONIOX_TRANSCRIPTION_TIMEOUT_MS = 200_000;

// POST /api/stt/soniox
export async function postSonioxTranscription(
audio: Blob,
fileName: string,
language: string,
): Promise<PostSonioxTranscriptionResponseType> {
const body = new FormData();
body.append('audio', audio, fileName);
body.append('language', language);
const response = await request<PostSonioxTranscriptionResponseType>(
'POST',
`${ApiUrl.stt}/soniox`,
body,
null,
{
headers: { 'Content-Type': 'multipart/form-data' },
timeout: SONIOX_TRANSCRIPTION_TIMEOUT_MS,
},
);

return response.data;
}
98 changes: 98 additions & 0 deletions src/apis/apis/transcript.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { http, HttpResponse } from 'msw';
import { server } from '../../mocks/server';
import { ApiUrl } from '../endpoints';
import { PostTranscriptRequestType } from '../requests/transcript';
import {
GetTranscriptResponseType,
GetTranscriptsResponseType,
} from '../responses/transcript';
import { getTranscript, getTranscripts, postTranscript } from './transcript';

const mockTranscript: GetTranscriptResponseType = {
id: 11,
tableId: 5,
tableName: '정기토론',
agenda: '노동시장 유연화를 확대해야 한다',
prosTeamName: '찬성',
consTeamName: '반대',
status: 'PROCESSING',
sttMode: 'SONIOX',
refining: true,
lines: [
{
index: 0,
timeBoxIndex: 0,
speechType: '입론',
stance: 'PROS',
speaker: '콜리',
offsetMs: 1000,
text: '찬성 입론입니다',
},
],
report: null,
errorMessage: null,
};

describe('토론 기록 API', () => {
it('테이블 ID와 발언 구간을 보내 토론 기록을 생성한다', async () => {
const body: PostTranscriptRequestType = {
segments: [
{
timeBoxIndex: 0,
team: null,
utterances: [{ offsetMs: 1000, text: '찬성 입론입니다' }],
},
],
sttMode: 'SONIOX',
};
server.use(
http.post(
`${ApiUrl.transcript}/:tableId`,
async ({ params, request }) => {
expect(params.tableId).toBe('5');
expect(await request.json()).toEqual(body);
return HttpResponse.json({ id: 11 }, { status: 201 });
},
),
);

const data = await postTranscript(5, body);

expect(data).toEqual({ id: 11 });
});

it('기록 ID로 속기록과 분석 상태를 조회한다', async () => {
server.use(
http.get(`${ApiUrl.transcript}/:transcriptId`, ({ params }) => {
expect(params.transcriptId).toBe('11');
return HttpResponse.json(mockTranscript);
}),
);

const data = await getTranscript(11);

expect(data).toEqual(mockTranscript);
});

it('회원의 토론 기록 목록을 조회한다', async () => {
const response: GetTranscriptsResponseType = {
transcripts: [
{
id: 12,
tableId: 5,
tableName: '정기토론',
agenda: '노동시장 유연화를 확대해야 한다',
sttMode: 'SONIOX',
status: 'PROCESSING',
refining: true,
createdAt: '2026-10-23T01:39:15',
},
],
};
server.use(http.get(ApiUrl.transcript, () => HttpResponse.json(response)));

const data = await getTranscripts();

expect(data).toEqual(response);
});
});
51 changes: 51 additions & 0 deletions src/apis/apis/transcript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { ApiUrl } from '../endpoints';
import { request } from '../primitives';
import { PostTranscriptRequestType } from '../requests/transcript';
import {
GetTranscriptResponseType,
GetTranscriptsResponseType,
PostTranscriptResponseType,
} from '../responses/transcript';

// POST /api/transcripts/{tableId}
export async function postTranscript(
tableId: number,
body: PostTranscriptRequestType,
): Promise<PostTranscriptResponseType> {
const requestUrl = `${ApiUrl.transcript}/${encodeURIComponent(tableId)}`;
const response = await request<PostTranscriptResponseType>(
'POST',
requestUrl,
body,
null,
);

return response.data;
}

// GET /api/transcripts/{transcriptId}
export async function getTranscript(
transcriptId: number,
): Promise<GetTranscriptResponseType> {
const requestUrl = `${ApiUrl.transcript}/${encodeURIComponent(transcriptId)}`;
const response = await request<GetTranscriptResponseType>(
'GET',
requestUrl,
null,
null,
);

return response.data;
}

// GET /api/transcripts
export async function getTranscripts(): Promise<GetTranscriptsResponseType> {
const response = await request<GetTranscriptsResponseType>(
'GET',
ApiUrl.transcript,
null,
null,
);

return response.data;
}
Loading
Loading