opensoma 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/package.json +2 -2
  2. package/dist/src/cli.d.ts.map +1 -1
  3. package/dist/src/cli.js +2 -1
  4. package/dist/src/cli.js.map +1 -1
  5. package/dist/src/client.d.ts +24 -1
  6. package/dist/src/client.d.ts.map +1 -1
  7. package/dist/src/client.js +159 -4
  8. package/dist/src/client.js.map +1 -1
  9. package/dist/src/commands/auth.d.ts +5 -1
  10. package/dist/src/commands/auth.d.ts.map +1 -1
  11. package/dist/src/commands/auth.js +83 -4
  12. package/dist/src/commands/auth.js.map +1 -1
  13. package/dist/src/commands/helpers.d.ts +5 -1
  14. package/dist/src/commands/helpers.d.ts.map +1 -1
  15. package/dist/src/commands/helpers.js +28 -3
  16. package/dist/src/commands/helpers.js.map +1 -1
  17. package/dist/src/commands/index.d.ts +1 -0
  18. package/dist/src/commands/index.d.ts.map +1 -1
  19. package/dist/src/commands/index.js +1 -0
  20. package/dist/src/commands/index.js.map +1 -1
  21. package/dist/src/commands/mentoring.d.ts.map +1 -1
  22. package/dist/src/commands/mentoring.js +44 -2
  23. package/dist/src/commands/mentoring.js.map +1 -1
  24. package/dist/src/commands/report.d.ts +27 -0
  25. package/dist/src/commands/report.d.ts.map +1 -0
  26. package/dist/src/commands/report.js +224 -0
  27. package/dist/src/commands/report.js.map +1 -0
  28. package/dist/src/commands/room.d.ts.map +1 -1
  29. package/dist/src/commands/room.js +25 -2
  30. package/dist/src/commands/room.js.map +1 -1
  31. package/dist/src/constants.d.ts +14 -9
  32. package/dist/src/constants.d.ts.map +1 -1
  33. package/dist/src/constants.js +25 -9
  34. package/dist/src/constants.js.map +1 -1
  35. package/dist/src/formatters.d.ts +4 -1
  36. package/dist/src/formatters.d.ts.map +1 -1
  37. package/dist/src/formatters.js +128 -24
  38. package/dist/src/formatters.js.map +1 -1
  39. package/dist/src/http.d.ts +5 -0
  40. package/dist/src/http.d.ts.map +1 -1
  41. package/dist/src/http.js +107 -2
  42. package/dist/src/http.js.map +1 -1
  43. package/dist/src/shared/utils/html.d.ts +3 -0
  44. package/dist/src/shared/utils/html.d.ts.map +1 -0
  45. package/dist/src/shared/utils/html.js +12 -0
  46. package/dist/src/shared/utils/html.js.map +1 -0
  47. package/dist/src/shared/utils/report-params.d.ts +11 -0
  48. package/dist/src/shared/utils/report-params.d.ts.map +1 -0
  49. package/dist/src/shared/utils/report-params.js +27 -0
  50. package/dist/src/shared/utils/report-params.js.map +1 -0
  51. package/dist/src/shared/utils/swmaestro.d.ts +26 -0
  52. package/dist/src/shared/utils/swmaestro.d.ts.map +1 -1
  53. package/dist/src/shared/utils/swmaestro.js +77 -6
  54. package/dist/src/shared/utils/swmaestro.js.map +1 -1
  55. package/dist/src/token-extractor.d.ts.map +1 -1
  56. package/dist/src/token-extractor.js +38 -8
  57. package/dist/src/token-extractor.js.map +1 -1
  58. package/dist/src/types.d.ts +156 -0
  59. package/dist/src/types.d.ts.map +1 -1
  60. package/dist/src/types.js +90 -1
  61. package/dist/src/types.js.map +1 -1
  62. package/package.json +2 -2
  63. package/src/cli.ts +2 -0
  64. package/src/client.test.ts +92 -20
  65. package/src/client.ts +192 -4
  66. package/src/commands/auth.test.ts +40 -1
  67. package/src/commands/auth.ts +91 -3
  68. package/src/commands/helpers.test.ts +108 -4
  69. package/src/commands/helpers.ts +30 -3
  70. package/src/commands/index.ts +1 -0
  71. package/src/commands/mentoring.ts +64 -1
  72. package/src/commands/report.test.ts +49 -0
  73. package/src/commands/report.ts +322 -0
  74. package/src/commands/room.ts +31 -3
  75. package/src/constants.ts +26 -9
  76. package/src/formatters.test.ts +293 -2
  77. package/src/formatters.ts +152 -28
  78. package/src/http.test.ts +373 -1
  79. package/src/http.ts +122 -2
  80. package/src/shared/utils/html.ts +12 -0
  81. package/src/shared/utils/report-params.ts +41 -0
  82. package/src/shared/utils/swmaestro.test.ts +44 -0
  83. package/src/shared/utils/swmaestro.ts +106 -9
  84. package/src/token-extractor.ts +59 -20
  85. package/src/types.test.ts +101 -1
  86. package/src/types.ts +106 -1
package/dist/src/types.js CHANGED
@@ -1,7 +1,12 @@
1
1
  import { z } from 'zod/v4';
2
2
  const DateRangeSchema = z.object({ start: z.string(), end: z.string() });
3
3
  const TimeRangeSchema = z.object({ start: z.string(), end: z.string() });
4
- const RoomTimeSlotSchema = z.object({ time: z.string(), available: z.boolean() });
4
+ const RoomTimeSlotReservationSchema = z.object({ title: z.string(), bookedBy: z.string() });
5
+ const RoomTimeSlotSchema = z.object({
6
+ time: z.string(),
7
+ available: z.boolean(),
8
+ reservation: RoomTimeSlotReservationSchema.optional(),
9
+ });
5
10
  const DashboardStatusItemSchema = z.object({
6
11
  title: z.string(),
7
12
  url: z.string(),
@@ -30,9 +35,16 @@ export const MentoringListItemSchema = z.object({
30
35
  author: z.string(),
31
36
  createdAt: z.string(),
32
37
  });
38
+ export const MentoringApplicantSchema = z.object({
39
+ name: z.string(),
40
+ appliedAt: z.string(),
41
+ cancelledAt: z.string(),
42
+ status: z.string(),
43
+ });
33
44
  export const MentoringDetailSchema = MentoringListItemSchema.extend({
34
45
  content: z.string(),
35
46
  venue: z.string(),
47
+ applicants: z.array(MentoringApplicantSchema),
36
48
  });
37
49
  export const RoomCardSchema = z.object({
38
50
  itemId: z.number(),
@@ -114,4 +126,81 @@ export const CredentialsSchema = z.object({
114
126
  password: z.string().optional(),
115
127
  loggedInAt: z.string().optional(),
116
128
  });
129
+ export const ReportListItemSchema = z.object({
130
+ id: z.number(),
131
+ category: z.string(),
132
+ title: z.string(),
133
+ progressDate: z.string(),
134
+ status: z.string(),
135
+ author: z.string(),
136
+ createdAt: z.string(),
137
+ acceptedTime: z.string(),
138
+ payAmount: z.string(),
139
+ });
140
+ export const ReportDetailSchema = ReportListItemSchema.extend({
141
+ content: z.string(),
142
+ subject: z.string(),
143
+ menteeRegion: z.string(),
144
+ reportType: z.string(),
145
+ teamNames: z.string(),
146
+ venue: z.string(),
147
+ attendanceCount: z.number(),
148
+ attendanceNames: z.string(),
149
+ progressStartTime: z.string(),
150
+ progressEndTime: z.string(),
151
+ exceptStartTime: z.string(),
152
+ exceptEndTime: z.string(),
153
+ exceptReason: z.string(),
154
+ mentorOpinion: z.string(),
155
+ nonAttendanceNames: z.string(),
156
+ etc: z.string(),
157
+ files: z.array(z.string()),
158
+ });
159
+ export const ApprovalListItemSchema = z.object({
160
+ id: z.number(),
161
+ category: z.string(),
162
+ title: z.string(),
163
+ progressDate: z.string(),
164
+ status: z.string(),
165
+ author: z.string(),
166
+ createdAt: z.string(),
167
+ acceptedTime: z.string(),
168
+ travelExpense: z.string(),
169
+ mentoringAllowance: z.string(),
170
+ });
171
+ export const ReportCreateOptionsSchema = z.object({
172
+ menteeRegion: z.enum(['S', 'B']),
173
+ reportType: z.enum(['MRC010', 'MRC020']),
174
+ progressDate: z.string(),
175
+ teamNames: z.string().optional(),
176
+ venue: z.string(),
177
+ attendanceCount: z.number(),
178
+ attendanceNames: z.string(),
179
+ progressStartTime: z.string(),
180
+ progressEndTime: z.string(),
181
+ exceptStartTime: z.string().optional(),
182
+ exceptEndTime: z.string().optional(),
183
+ exceptReason: z.string().optional(),
184
+ subject: z.string().min(10),
185
+ content: z.string().min(100),
186
+ mentorOpinion: z.string().optional(),
187
+ nonAttendanceNames: z.string().optional(),
188
+ etc: z.string().optional(),
189
+ });
190
+ export const ReportUpdateOptionsSchema = ReportCreateOptionsSchema.partial().extend({
191
+ id: z.number(),
192
+ });
193
+ export const MentoringCreateOptionsSchema = z.object({
194
+ title: z.string(),
195
+ type: z.enum(['public', 'lecture']),
196
+ date: z.string(),
197
+ startTime: z.string(),
198
+ endTime: z.string(),
199
+ venue: z.string(),
200
+ maxAttendees: z.number().optional(),
201
+ regStart: z.string().optional(),
202
+ regEnd: z.string().optional(),
203
+ content: z.string().optional(),
204
+ });
205
+ export const MentoringUpdateOptionsSchema = MentoringCreateOptionsSchema.partial();
117
206
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "opensoma",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "SWMaestro MyPage CLI & SDK for AI agents",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/devxoul/opensoma.git"
8
+ "url": "git+https://github.com/opensoma/opensoma.git"
9
9
  },
10
10
  "bin": {
11
11
  "opensoma": "dist/src/cli.js"
package/src/cli.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  memberCommand,
11
11
  mentoringCommand,
12
12
  noticeCommand,
13
+ reportCommand,
13
14
  roomCommand,
14
15
  teamCommand,
15
16
  } from './commands/index'
@@ -51,6 +52,7 @@ program.addCommand(noticeCommand)
51
52
  program.addCommand(teamCommand)
52
53
  program.addCommand(memberCommand)
53
54
  program.addCommand(eventCommand)
55
+ program.addCommand(reportCommand)
54
56
 
55
57
  program.parse(process.argv)
56
58
 
@@ -72,14 +72,19 @@ describe('SomaClient', () => {
72
72
  })
73
73
 
74
74
  test('mutating operations post expected payloads', async () => {
75
+ const mentoringDetailHtml =
76
+ '<div class="group"><strong class="t">모집 명</strong><div class="c">[자유 멘토링] 기존 멘토링</div></div><div class="group"><strong class="t">접수 기간</strong><div class="c">2026.03.01 ~ 2026.03.15</div></div><div class="group"><strong class="t">강의날짜</strong><div class="c"><span>2026.03.20 10:00시 ~ 12:00시</span></div></div><div class="group"><strong class="t">장소</strong><div class="c">온라인(Webex)</div></div><div class="group"><strong class="t">모집인원</strong><div class="c">5명</div></div><div class="group"><strong class="t">작성자</strong><div class="c">전수열</div></div><div class="group"><strong class="t">등록일</strong><div class="c">2026.03.01</div></div><div class="cont"><p>기존 내용</p></div>'
75
77
  const client = new SomaClient()
76
- const calls: Array<{ path: string; data: Record<string, string> }> = []
78
+ const calls: Array<{ method: string; path: string; data: Record<string, string> }> = []
79
+ const captor = (method: string) => async (path: string, data: Record<string, string>) => {
80
+ calls.push({ method, path, data })
81
+ return ''
82
+ }
77
83
  Reflect.set(client, 'http', {
78
84
  checkLogin: async () => ({ userId: 'user@example.com', userNm: 'Test' }),
79
- post: async (path: string, data: Record<string, string>) => {
80
- calls.push({ path, data })
81
- return ''
82
- },
85
+ get: async () => mentoringDetailHtml,
86
+ post: captor('post'),
87
+ postForm: captor('postForm'),
83
88
  })
84
89
 
85
90
  await client.mentoring.create({
@@ -91,6 +96,14 @@ describe('SomaClient', () => {
91
96
  venue: '온라인(Webex)',
92
97
  maxAttendees: 10,
93
98
  })
99
+ await client.mentoring.update(42, {
100
+ title: '수정된 멘토링',
101
+ type: 'public',
102
+ date: '2026-05-01',
103
+ startTime: '14:00',
104
+ endTime: '15:00',
105
+ venue: '오프라인',
106
+ })
94
107
  await client.mentoring.delete(7)
95
108
  await client.mentoring.apply(8)
96
109
  await client.mentoring.cancel({ applySn: 9, qustnrSn: 10 })
@@ -102,43 +115,50 @@ describe('SomaClient', () => {
102
115
  })
103
116
  await client.event.apply(11)
104
117
 
105
- expect(calls.map((call) => call.path)).toEqual([
106
- '/mypage/mentoLec/insert.do',
107
- '/mypage/mentoLec/delete.do',
108
- '/application/application/application.do',
109
- '/mypage/userAnswer/cancel.do',
110
- '/mypage/itemRent/insert.do',
111
- '/application/application/application.do',
118
+ expect(calls.map((call) => `${call.method}:${call.path}`)).toEqual([
119
+ 'postForm:/mypage/mentoLec/insert.do',
120
+ 'postForm:/mypage/mentoLec/update.do',
121
+ 'post:/mypage/mentoLec/delete.do',
122
+ 'post:/application/application/application.do',
123
+ 'post:/mypage/userAnswer/cancel.do',
124
+ 'post:/mypage/itemRent/insert.do',
125
+ 'post:/application/application/application.do',
112
126
  ])
113
127
  expect(calls[0]?.data).toMatchObject({
114
128
  menuNo: MENU_NO.MENTORING,
115
129
  reportCd: 'MRC020',
116
130
  qustnrSj: '새 멘토링',
117
131
  })
118
- expect(calls[1]?.data).toEqual({
132
+ expect(calls[1]?.data).toMatchObject({
133
+ menuNo: MENU_NO.MENTORING,
134
+ reportCd: 'MRC010',
135
+ qustnrSj: '수정된 멘토링',
136
+ qustnrSn: '42',
137
+ })
138
+ expect(calls[2]?.data).toEqual({
119
139
  menuNo: MENU_NO.MENTORING,
120
140
  qustnrSn: '7',
121
141
  pageQueryString: '',
122
142
  })
123
- expect(calls[2]?.data).toEqual({
143
+ expect(calls[3]?.data).toEqual({
124
144
  menuNo: MENU_NO.EVENT,
125
145
  qustnrSn: '8',
126
146
  applyGb: 'C',
127
147
  stepHeader: '0',
128
148
  })
129
- expect(calls[3]?.data).toEqual({
149
+ expect(calls[4]?.data).toEqual({
130
150
  menuNo: MENU_NO.APPLICATION_HISTORY,
131
151
  applySn: '9',
132
152
  qustnrSn: '10',
133
153
  })
134
- expect(calls[4]?.data).toMatchObject({
154
+ expect(calls[5]?.data).toMatchObject({
135
155
  menuNo: MENU_NO.ROOM,
136
156
  itemId: '17',
137
157
  title: '회의',
138
158
  'time[0]': '10:00',
139
159
  'time[1]': '10:30',
140
160
  })
141
- expect(calls[5]?.data).toEqual({
161
+ expect(calls[6]?.data).toEqual({
142
162
  menuNo: MENU_NO.EVENT,
143
163
  qustnrSn: '11',
144
164
  applyGb: 'C',
@@ -146,6 +166,38 @@ describe('SomaClient', () => {
146
166
  })
147
167
  })
148
168
 
169
+ test('mentoring.update merges partial params with existing data', async () => {
170
+ const mentoringDetailHtml =
171
+ '<div class="group"><strong class="t">모집 명</strong><div class="c">[멘토 특강] 웹 성능 특강</div></div><div class="group"><strong class="t">접수 기간</strong><div class="c">2026.04.01 ~ 2026.04.10</div></div><div class="group"><strong class="t">강의날짜</strong><div class="c"><span>2026.04.11 14:00시 ~ 15:30시</span></div></div><div class="group"><strong class="t">장소</strong><div class="c">온라인(Webex)</div></div><div class="group"><strong class="t">모집인원</strong><div class="c">20명</div></div><div class="group"><strong class="t">작성자</strong><div class="c">전수열</div></div><div class="group"><strong class="t">등록일</strong><div class="c">2026.04.01</div></div><div class="cont"><p>세션 본문</p></div>'
172
+ const client = new SomaClient()
173
+ const postFormCalls: Array<{ path: string; data: Record<string, string> }> = []
174
+ Reflect.set(client, 'http', {
175
+ checkLogin: async () => ({ userId: 'user@example.com', userNm: 'Test' }),
176
+ get: async () => mentoringDetailHtml,
177
+ postForm: async (path: string, data: Record<string, string>) => {
178
+ postFormCalls.push({ path, data })
179
+ return ''
180
+ },
181
+ })
182
+
183
+ await client.mentoring.update(9572, { title: '변경된 제목' })
184
+
185
+ expect(postFormCalls).toHaveLength(1)
186
+ expect(postFormCalls[0]?.data).toMatchObject({
187
+ qustnrSj: '변경된 제목',
188
+ qustnrSn: '9572',
189
+ reportCd: 'MRC020',
190
+ eventDt: '2026-04-11',
191
+ eventStime: '14:00',
192
+ eventEtime: '15:30',
193
+ place: '온라인(Webex)',
194
+ applyCnt: '20',
195
+ bgnde: '2026-04-01',
196
+ endde: '2026-04-10',
197
+ qestnarCn: '<p>세션 본문</p>',
198
+ })
199
+ })
200
+
149
201
  test('room, dashboard, notice, team, member, event, and history routes use expected endpoints', async () => {
150
202
  const client = new SomaClient()
151
203
  const calls: Array<{ method: string; path: string; data: Record<string, string> | undefined }> = []
@@ -182,13 +234,13 @@ describe('SomaClient', () => {
182
234
  post: async (path: string, data: Record<string, string>) => {
183
235
  calls.push({ method: 'post', path, data })
184
236
  if (path === '/mypage/officeMng/rentTime.do') {
185
- return '<div class="time-grid"><span>09:00</span></div>'
237
+ return '<span class="ck-st2 disabled" data-hour="09" data-minute="00"><input type="checkbox" disabled="disabled"><label title="아침 회의&lt;br&gt;예약자 : 김오픈">오전 09:00</label></span>'
186
238
  }
187
239
  return '<ul class="bbs-reserve"><li class="item"><a href="javascript:void(0);" onclick="location.href=\'/sw/mypage/officeMng/view.do?itemId=17\';"><div class="cont"><h4 class="tit">스페이스 A1</h4><ul class="txt bul-dot grey"><li>이용기간 : 2026-04-01 ~ 2026-12-31</li><li><p>설명 : 4인</p></li><li class="time-list"><div class="time-grid"><span>09:00</span></div></li></ul></div></a></li></ul>'
188
240
  },
189
241
  })
190
242
 
191
- const roomList = await client.room.list({ date: '2026-04-01', room: 'A1' })
243
+ const roomList = await client.room.list({ date: '2026-04-01', room: 'A1', includeReservations: true })
192
244
  const roomSlots = await client.room.available(17, '2026-04-01')
193
245
  const dashboard = await client.dashboard.get()
194
246
  const noticeList = await client.notice.list({ page: 2 })
@@ -200,7 +252,12 @@ describe('SomaClient', () => {
200
252
  const history = await client.mentoring.history({ page: 4 })
201
253
 
202
254
  expect(roomList[0]?.itemId).toBe(17)
203
- expect(roomSlots).toEqual([{ time: '09:00', available: true }])
255
+ expect(roomList[0]?.timeSlots).toEqual([
256
+ { time: '09:00', available: false, reservation: { title: '아침 회의', bookedBy: '김오픈' } },
257
+ ])
258
+ expect(roomSlots).toEqual([
259
+ { time: '09:00', available: false, reservation: { title: '아침 회의', bookedBy: '김오픈' } },
260
+ ])
204
261
  expect(dashboard.name).toBe('전수열')
205
262
  expect(dashboard.mentoringSessions).toEqual([
206
263
  {
@@ -234,6 +291,11 @@ describe('SomaClient', () => {
234
291
 
235
292
  const dashboardCallIndex = calls.findIndex((c) => c.path === '/mypage/myMain/dashboard.do')
236
293
  expect(dashboardCallIndex).toBeGreaterThanOrEqual(0)
294
+ expect(calls).toContainEqual({
295
+ method: 'post',
296
+ path: '/mypage/officeMng/rentTime.do',
297
+ data: { viewType: 'CONTBODY', itemId: '17', rentDt: '2026-04-01' },
298
+ })
237
299
  const mentoringListCall = calls.find((c) => c.path === '/mypage/mentoLec/list.do')
238
300
  expect(mentoringListCall?.data).toEqual({
239
301
  menuNo: MENU_NO.MENTORING,
@@ -337,6 +399,16 @@ describe('SomaClient', () => {
337
399
  venue: 'Test',
338
400
  }),
339
401
  ).rejects.toBeInstanceOf(AuthenticationError)
402
+ await expect(
403
+ client.mentoring.update(1, {
404
+ title: 'Test',
405
+ type: 'public',
406
+ date: '2026-04-01',
407
+ startTime: '10:00',
408
+ endTime: '11:00',
409
+ venue: 'Test',
410
+ }),
411
+ ).rejects.toBeInstanceOf(AuthenticationError)
340
412
  await expect(client.mentoring.delete(1)).rejects.toBeInstanceOf(AuthenticationError)
341
413
  await expect(client.mentoring.apply(1)).rejects.toBeInstanceOf(AuthenticationError)
342
414
  await expect(client.mentoring.cancel({ applySn: 1, qustnrSn: 2 })).rejects.toBeInstanceOf(AuthenticationError)
package/src/client.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { readFile } from 'node:fs/promises'
2
+
1
3
  import { MENU_NO } from './constants'
2
4
  import { CredentialManager } from './credential-manager'
3
5
  import { AuthenticationError } from './errors'
@@ -9,20 +11,31 @@ import {
9
11
  buildCancelApplicationPayload,
10
12
  buildDeleteMentoringPayload,
11
13
  buildMentoringPayload,
14
+ buildReportPayload,
12
15
  buildRoomReservationPayload,
16
+ buildUpdateMentoringPayload,
13
17
  parseEventDetail,
14
18
  resolveRoomId,
19
+ toMentoringType,
20
+ toRegionCode,
21
+ toReportTypeCd,
15
22
  } from './shared/utils/swmaestro'
16
23
  import type {
17
24
  ApplicationHistoryItem,
25
+ ApprovalListItem,
18
26
  Dashboard,
19
27
  EventListItem,
20
28
  MemberInfo,
21
29
  MentoringDetail,
22
30
  MentoringListItem,
31
+ MentoringUpdateOptions,
23
32
  NoticeDetail,
24
33
  NoticeListItem,
25
34
  Pagination,
35
+ ReportCreateOptions,
36
+ ReportDetail,
37
+ ReportListItem,
38
+ ReportUpdateOptions,
26
39
  RoomCard,
27
40
  TeamInfo,
28
41
  } from './types'
@@ -60,6 +73,7 @@ export class SomaClient {
60
73
  regEnd?: string
61
74
  content?: string
62
75
  }): Promise<void>
76
+ update(id: number, params: MentoringUpdateOptions): Promise<void>
63
77
  delete(id: number): Promise<void>
64
78
  apply(id: number): Promise<void>
65
79
  cancel(params: { applySn: number; qustnrSn: number }): Promise<void>
@@ -67,7 +81,7 @@ export class SomaClient {
67
81
  }
68
82
 
69
83
  readonly room: {
70
- list(options?: { date?: string; room?: string }): Promise<RoomCard[]>
84
+ list(options?: { date?: string; room?: string; includeReservations?: boolean }): Promise<RoomCard[]>
71
85
  available(roomId: number, date: string): Promise<RoomCard['timeSlots']>
72
86
  reserve(params: {
73
87
  roomId: number
@@ -88,6 +102,27 @@ export class SomaClient {
88
102
  get(id: number): Promise<NoticeDetail>
89
103
  }
90
104
 
105
+ readonly report: {
106
+ list(options?: {
107
+ page?: number
108
+ searchField?: string
109
+ searchKeyword?: string
110
+ }): Promise<{ items: ReportListItem[]; pagination: Pagination }>
111
+ get(id: number): Promise<ReportDetail>
112
+ create(options: ReportCreateOptions, file: Buffer | string, fileName?: string): Promise<void>
113
+ update(
114
+ id: number,
115
+ options: Omit<ReportUpdateOptions, 'id'>,
116
+ file?: Buffer | string,
117
+ fileName?: string,
118
+ ): Promise<void>
119
+ approval(options?: {
120
+ page?: number
121
+ month?: string
122
+ reportType?: string
123
+ }): Promise<{ items: ApprovalListItem[]; pagination: Pagination }>
124
+ }
125
+
91
126
  readonly team: {
92
127
  show(): Promise<TeamInfo>
93
128
  }
@@ -143,11 +178,31 @@ export class SomaClient {
143
178
  },
144
179
  create: async (params) => {
145
180
  await this.requireAuth()
146
- const html = await this.http.post('/mypage/mentoLec/insert.do', buildMentoringPayload(params))
181
+ const html = await this.http.postForm('/mypage/mentoLec/insert.do', buildMentoringPayload(params))
147
182
  if (this.containsErrorIndicator(html)) {
148
183
  throw new Error(this.extractErrorMessage(html) || '멘토링 등록에 실패했습니다.')
149
184
  }
150
185
  },
186
+ update: async (id, params) => {
187
+ await this.requireAuth()
188
+ const existing = await this.mentoring.get(id)
189
+ const merged = buildUpdateMentoringPayload(id, {
190
+ title: params.title ?? existing.title,
191
+ type: params.type ?? toMentoringType(existing.type),
192
+ date: params.date ?? existing.sessionDate,
193
+ startTime: params.startTime ?? existing.sessionTime.start,
194
+ endTime: params.endTime ?? existing.sessionTime.end,
195
+ venue: params.venue ?? existing.venue,
196
+ maxAttendees: params.maxAttendees ?? existing.attendees.max,
197
+ regStart: params.regStart ?? existing.registrationPeriod.start,
198
+ regEnd: params.regEnd ?? existing.registrationPeriod.end,
199
+ content: params.content ?? existing.content,
200
+ })
201
+ const html = await this.http.postForm('/mypage/mentoLec/update.do', merged)
202
+ if (this.containsErrorIndicator(html)) {
203
+ throw new Error(this.extractErrorMessage(html) || '멘토링 수정에 실패했습니다.')
204
+ }
205
+ },
151
206
  delete: async (id) => {
152
207
  await this.requireAuth()
153
208
  await this.http.post('/mypage/mentoLec/delete.do', buildDeleteMentoringPayload(id))
@@ -176,13 +231,35 @@ export class SomaClient {
176
231
  this.room = {
177
232
  list: async (options) => {
178
233
  await this.requireAuth()
179
- return formatters.parseRoomList(
234
+ const date = options?.date ?? new Date().toISOString().slice(0, 10)
235
+ const rooms = formatters.parseRoomList(
180
236
  await this.http.post('/mypage/officeMng/list.do', {
181
237
  menuNo: MENU_NO.ROOM,
182
- sdate: options?.date ?? new Date().toISOString().slice(0, 10),
238
+ sdate: date,
183
239
  searchItemId: options?.room ? String(resolveRoomId(options.room)) : '',
184
240
  }),
185
241
  )
242
+
243
+ if (!options?.includeReservations) return rooms
244
+
245
+ return Promise.all(
246
+ rooms.map(async (room) => {
247
+ try {
248
+ const html = await this.http.post('/mypage/officeMng/rentTime.do', {
249
+ viewType: 'CONTBODY',
250
+ itemId: String(room.itemId),
251
+ rentDt: date,
252
+ })
253
+
254
+ return {
255
+ ...room,
256
+ timeSlots: formatters.parseRoomSlots(html),
257
+ }
258
+ } catch {
259
+ return room
260
+ }
261
+ }),
262
+ )
186
263
  },
187
264
  available: async (roomId, date) => {
188
265
  await this.requireAuth()
@@ -244,6 +321,117 @@ export class SomaClient {
244
321
  },
245
322
  }
246
323
 
324
+ this.report = {
325
+ list: async (options) => {
326
+ await this.requireAuth()
327
+ const params: Record<string, string> = {
328
+ menuNo: MENU_NO.REPORT,
329
+ pageIndex: String(options?.page ?? 1),
330
+ }
331
+ if (options?.searchField !== undefined) params.searchCnd = options.searchField
332
+ if (options?.searchKeyword) params.searchWrd = options.searchKeyword
333
+ const html = await this.http.get('/mypage/mentoringReport/list.do', params)
334
+ return {
335
+ items: formatters.parseReportList(html),
336
+ pagination: formatters.parsePagination(html),
337
+ }
338
+ },
339
+ get: async (id) => {
340
+ await this.requireAuth()
341
+ const html = await this.http.get('/mypage/mentoringReport/view.do', {
342
+ menuNo: MENU_NO.REPORT,
343
+ reportId: String(id),
344
+ })
345
+ return formatters.parseReportDetail(html, id)
346
+ },
347
+ create: async (options, file, fileName) => {
348
+ await this.requireAuth()
349
+ const payload = buildReportPayload({
350
+ menteeRegion: options.menteeRegion,
351
+ reportType: options.reportType,
352
+ progressDate: options.progressDate,
353
+ teamNames: options.teamNames,
354
+ venue: options.venue,
355
+ attendanceCount: options.attendanceCount,
356
+ attendanceNames: options.attendanceNames,
357
+ progressStartTime: options.progressStartTime,
358
+ progressEndTime: options.progressEndTime,
359
+ exceptStartTime: options.exceptStartTime,
360
+ exceptEndTime: options.exceptEndTime,
361
+ exceptReason: options.exceptReason,
362
+ subject: options.subject,
363
+ content: options.content,
364
+ mentorOpinion: options.mentorOpinion,
365
+ nonAttendanceNames: options.nonAttendanceNames,
366
+ etc: options.etc,
367
+ })
368
+ const formData = new FormData()
369
+ for (const [key, value] of Object.entries(payload)) {
370
+ formData.append(key, value)
371
+ }
372
+ const isBuffer = Buffer.isBuffer(file)
373
+ const fileBuffer = isBuffer ? file : await readFile(file)
374
+ const resolvedFileName = isBuffer ? (fileName ?? 'file') : (file.split('/').pop() ?? 'file')
375
+ const uint8Array = new Uint8Array(fileBuffer.buffer, fileBuffer.byteOffset, fileBuffer.byteLength)
376
+ formData.append('file_1_1', new Blob([uint8Array as unknown as ArrayBuffer]), resolvedFileName)
377
+ formData.append('fileFieldNm_1', 'file_1')
378
+ formData.append('atchFileId', '')
379
+ await this.http.postMultipart('/mypage/mentoringReport/insert.do', formData)
380
+ },
381
+ update: async (id, options, file, fileName) => {
382
+ await this.requireAuth()
383
+ const existing = await this.report.get(id)
384
+ const payload = buildReportPayload({
385
+ menteeRegion: options.menteeRegion ?? toRegionCode(existing.menteeRegion),
386
+ reportType: options.reportType ?? toReportTypeCd(existing.reportType),
387
+ progressDate: options.progressDate ?? existing.progressDate,
388
+ teamNames: options.teamNames ?? existing.teamNames,
389
+ venue: options.venue ?? existing.venue,
390
+ attendanceCount: options.attendanceCount ?? existing.attendanceCount,
391
+ attendanceNames: options.attendanceNames ?? existing.attendanceNames,
392
+ progressStartTime: options.progressStartTime ?? existing.progressStartTime,
393
+ progressEndTime: options.progressEndTime ?? existing.progressEndTime,
394
+ exceptStartTime: options.exceptStartTime ?? existing.exceptStartTime,
395
+ exceptEndTime: options.exceptEndTime ?? existing.exceptEndTime,
396
+ exceptReason: options.exceptReason ?? existing.exceptReason,
397
+ subject: options.subject ?? existing.subject,
398
+ content: options.content ?? existing.content,
399
+ mentorOpinion: options.mentorOpinion ?? existing.mentorOpinion,
400
+ nonAttendanceNames: options.nonAttendanceNames ?? existing.nonAttendanceNames,
401
+ etc: options.etc ?? existing.etc,
402
+ reportId: id,
403
+ })
404
+ const formData = new FormData()
405
+ for (const [key, value] of Object.entries(payload)) {
406
+ formData.append(key, value)
407
+ }
408
+ if (file) {
409
+ const isBuffer = Buffer.isBuffer(file)
410
+ const fileBuffer = isBuffer ? file : await readFile(file)
411
+ const resolvedFileName = isBuffer ? (fileName ?? 'file') : (file.split('/').pop() ?? 'file')
412
+ const uint8Array = new Uint8Array(fileBuffer.buffer, fileBuffer.byteOffset, fileBuffer.byteLength)
413
+ formData.append('file_1_1', new Blob([uint8Array as unknown as ArrayBuffer]), resolvedFileName)
414
+ formData.append('fileFieldNm_1', 'file_1')
415
+ formData.append('atchFileId', '')
416
+ }
417
+ await this.http.postMultipart('/mypage/mentoringReport/update.do', formData)
418
+ },
419
+ approval: async (options) => {
420
+ await this.requireAuth()
421
+ const params: Record<string, string> = {
422
+ menuNo: MENU_NO.REPORT_APPROVAL,
423
+ pageIndex: String(options?.page ?? 1),
424
+ }
425
+ if (options?.month) params.searchMonth = options.month
426
+ if (options?.reportType !== undefined) params.searchReport = options.reportType
427
+ const html = await this.http.get('/mypage/mentoringReport/resultList.do', params)
428
+ return {
429
+ items: formatters.parseApprovalList(html),
430
+ pagination: formatters.parsePagination(html),
431
+ }
432
+ },
433
+ }
434
+
247
435
  this.team = {
248
436
  show: async () => {
249
437
  await this.requireAuth()