@spfn/cms 0.1.0-alpha.6 → 0.1.0-alpha.61
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.
- package/dist/actions.d.ts +140 -6
- package/dist/actions.js +97 -10
- package/dist/actions.js.map +1 -1
- package/dist/client.d.ts +43 -9
- package/dist/client.js +406 -56
- package/dist/client.js.map +1 -1
- package/dist/contracts/labels.d.ts +149 -0
- package/dist/contracts/labels.js +166 -0
- package/dist/contracts/labels.js.map +1 -0
- package/dist/contracts/published-cache.d.ts +25 -0
- package/dist/contracts/published-cache.js +32 -0
- package/dist/contracts/published-cache.js.map +1 -0
- package/dist/contracts/values.d.ts +69 -0
- package/dist/contracts/values.js +100 -0
- package/dist/contracts/values.js.map +1 -0
- package/dist/entities/cms-audit-logs.d.ts +15 -70
- package/dist/entities/cms-audit-logs.js +75 -100
- package/dist/entities/cms-audit-logs.js.map +1 -1
- package/dist/entities/cms-draft-cache.d.ts +13 -73
- package/dist/entities/cms-draft-cache.js +35 -109
- package/dist/entities/cms-draft-cache.js.map +1 -1
- package/dist/entities/cms-label-values.d.ts +14 -65
- package/dist/entities/cms-label-values.js +78 -102
- package/dist/entities/cms-label-values.js.map +1 -1
- package/dist/entities/cms-label-versions.d.ts +16 -49
- package/dist/entities/cms-label-versions.js +73 -77
- package/dist/entities/cms-label-versions.js.map +1 -1
- package/dist/entities/cms-labels.d.ts +17 -14
- package/dist/entities/cms-labels.js +39 -45
- package/dist/entities/cms-labels.js.map +1 -1
- package/dist/entities/cms-published-cache.d.ts +14 -69
- package/dist/entities/cms-published-cache.js +33 -100
- package/dist/entities/cms-published-cache.js.map +1 -1
- package/dist/entities/index.d.ts +7 -10
- package/dist/entities/index.js +217 -9
- package/dist/entities/index.js.map +1 -1
- package/dist/generators/index.d.ts +8 -7
- package/dist/generators/index.js +657 -17
- package/dist/generators/index.js.map +1 -1
- package/dist/index.d.ts +134 -20
- package/dist/index.js +1115 -23
- package/dist/index.js.map +1 -1
- package/dist/{generators/label-sync-generator.d.ts → label-sync-generator-lQrcVfja.d.ts} +9 -6
- package/dist/labels/index.d.ts +31 -4
- package/dist/labels/index.js +31 -6
- package/dist/labels/index.js.map +1 -1
- package/dist/repositories/index.d.ts +205 -6
- package/dist/repositories/index.js +435 -8
- package/dist/repositories/index.js.map +1 -1
- package/dist/routes/labels/[id]/index.js +499 -89
- package/dist/routes/labels/[id]/index.js.map +1 -1
- package/dist/routes/labels/{[id] → _id_}/index.d.ts +5 -3
- package/dist/routes/labels/by-key/[key]/index.js +453 -29
- package/dist/routes/labels/by-key/[key]/index.js.map +1 -1
- package/dist/routes/labels/by-key/_key_/index.d.ts +10 -0
- package/dist/routes/labels/index.d.ts +5 -3
- package/dist/routes/labels/index.js +488 -68
- package/dist/routes/labels/index.js.map +1 -1
- package/dist/routes/published-cache/index.d.ts +5 -3
- package/dist/routes/published-cache/index.js +325 -30
- package/dist/routes/published-cache/index.js.map +1 -1
- package/dist/routes/values/[labelId]/[version]/index.js +467 -41
- package/dist/routes/values/[labelId]/[version]/index.js.map +1 -1
- package/dist/routes/values/[labelId]/index.js +463 -39
- package/dist/routes/values/[labelId]/index.js.map +1 -1
- package/dist/routes/values/_labelId_/_version_/index.d.ts +10 -0
- package/dist/routes/values/_labelId_/index.d.ts +10 -0
- package/dist/server.d.ts +17 -7
- package/dist/server.js +263 -252
- package/dist/server.js.map +1 -1
- package/dist/store.d.ts +8 -14
- package/dist/store.js +396 -198
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +14 -7
- package/dist/types.js +0 -6
- package/dist/types.js.map +1 -1
- package/migrations/0000_condemned_centennial.sql +89 -0
- package/migrations/meta/0000_snapshot.json +687 -0
- package/migrations/meta/_journal.json +13 -0
- package/package.json +33 -16
- package/dist/actions.d.ts.map +0 -1
- package/dist/client.d.ts.map +0 -1
- package/dist/cms.config.d.ts +0 -77
- package/dist/cms.config.d.ts.map +0 -1
- package/dist/cms.config.js +0 -111
- package/dist/cms.config.js.map +0 -1
- package/dist/entities/cms-audit-logs.d.ts.map +0 -1
- package/dist/entities/cms-draft-cache.d.ts.map +0 -1
- package/dist/entities/cms-label-values.d.ts.map +0 -1
- package/dist/entities/cms-label-versions.d.ts.map +0 -1
- package/dist/entities/cms-labels.d.ts.map +0 -1
- package/dist/entities/cms-published-cache.d.ts.map +0 -1
- package/dist/entities/index.d.ts.map +0 -1
- package/dist/generators/index.d.ts.map +0 -1
- package/dist/generators/label-sync-generator.d.ts.map +0 -1
- package/dist/generators/label-sync-generator.js +0 -87
- package/dist/generators/label-sync-generator.js.map +0 -1
- package/dist/helpers/locale.actions.d.ts +0 -132
- package/dist/helpers/locale.actions.d.ts.map +0 -1
- package/dist/helpers/locale.actions.js +0 -210
- package/dist/helpers/locale.actions.js.map +0 -1
- package/dist/helpers/locale.constants.d.ts +0 -10
- package/dist/helpers/locale.constants.d.ts.map +0 -1
- package/dist/helpers/locale.constants.js +0 -10
- package/dist/helpers/locale.constants.js.map +0 -1
- package/dist/helpers/locale.d.ts +0 -17
- package/dist/helpers/locale.d.ts.map +0 -1
- package/dist/helpers/locale.js +0 -20
- package/dist/helpers/locale.js.map +0 -1
- package/dist/helpers/sync.d.ts +0 -41
- package/dist/helpers/sync.d.ts.map +0 -1
- package/dist/helpers/sync.js +0 -309
- package/dist/helpers/sync.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/init.d.ts +0 -31
- package/dist/init.d.ts.map +0 -1
- package/dist/init.js +0 -36
- package/dist/init.js.map +0 -1
- package/dist/labels/helpers.d.ts +0 -31
- package/dist/labels/helpers.d.ts.map +0 -1
- package/dist/labels/helpers.js +0 -60
- package/dist/labels/helpers.js.map +0 -1
- package/dist/labels/index.d.ts.map +0 -1
- package/dist/repositories/cms-draft-cache.repository.d.ts +0 -62
- package/dist/repositories/cms-draft-cache.repository.d.ts.map +0 -1
- package/dist/repositories/cms-draft-cache.repository.js +0 -56
- package/dist/repositories/cms-draft-cache.repository.js.map +0 -1
- package/dist/repositories/cms-label-values.repository.d.ts +0 -32
- package/dist/repositories/cms-label-values.repository.d.ts.map +0 -1
- package/dist/repositories/cms-label-values.repository.js +0 -72
- package/dist/repositories/cms-label-values.repository.js.map +0 -1
- package/dist/repositories/cms-labels.repository.d.ts +0 -53
- package/dist/repositories/cms-labels.repository.d.ts.map +0 -1
- package/dist/repositories/cms-labels.repository.js +0 -77
- package/dist/repositories/cms-labels.repository.js.map +0 -1
- package/dist/repositories/cms-published-cache.repository.d.ts +0 -53
- package/dist/repositories/cms-published-cache.repository.d.ts.map +0 -1
- package/dist/repositories/cms-published-cache.repository.js +0 -54
- package/dist/repositories/cms-published-cache.repository.js.map +0 -1
- package/dist/repositories/index.d.ts.map +0 -1
- package/dist/routes/labels/[id]/contract.d.ts +0 -68
- package/dist/routes/labels/[id]/contract.d.ts.map +0 -1
- package/dist/routes/labels/[id]/contract.js +0 -84
- package/dist/routes/labels/[id]/contract.js.map +0 -1
- package/dist/routes/labels/[id]/index.d.ts.map +0 -1
- package/dist/routes/labels/by-key/[key]/contract.d.ts +0 -24
- package/dist/routes/labels/by-key/[key]/contract.d.ts.map +0 -1
- package/dist/routes/labels/by-key/[key]/contract.js +0 -28
- package/dist/routes/labels/by-key/[key]/contract.js.map +0 -1
- package/dist/routes/labels/by-key/[key]/index.d.ts +0 -8
- package/dist/routes/labels/by-key/[key]/index.d.ts.map +0 -1
- package/dist/routes/labels/contract.d.ts +0 -59
- package/dist/routes/labels/contract.d.ts.map +0 -1
- package/dist/routes/labels/contract.js +0 -75
- package/dist/routes/labels/contract.js.map +0 -1
- package/dist/routes/labels/index.d.ts.map +0 -1
- package/dist/routes/published-cache/contract.d.ts +0 -25
- package/dist/routes/published-cache/contract.d.ts.map +0 -1
- package/dist/routes/published-cache/contract.js +0 -35
- package/dist/routes/published-cache/contract.js.map +0 -1
- package/dist/routes/published-cache/index.d.ts.map +0 -1
- package/dist/routes/values/[labelId]/[version]/contract.d.ts +0 -29
- package/dist/routes/values/[labelId]/[version]/contract.d.ts.map +0 -1
- package/dist/routes/values/[labelId]/[version]/contract.js +0 -33
- package/dist/routes/values/[labelId]/[version]/contract.js.map +0 -1
- package/dist/routes/values/[labelId]/[version]/index.d.ts +0 -8
- package/dist/routes/values/[labelId]/[version]/index.d.ts.map +0 -1
- package/dist/routes/values/[labelId]/contract.d.ts +0 -38
- package/dist/routes/values/[labelId]/contract.d.ts.map +0 -1
- package/dist/routes/values/[labelId]/contract.js +0 -59
- package/dist/routes/values/[labelId]/contract.js.map +0 -1
- package/dist/routes/values/[labelId]/index.d.ts +0 -8
- package/dist/routes/values/[labelId]/index.d.ts.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/store.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
package/dist/client.js
CHANGED
|
@@ -1,62 +1,412 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { client } from "@spfn/core/client";
|
|
3
|
+
|
|
4
|
+
// src/contracts/labels.ts
|
|
5
|
+
import { Type } from "@sinclair/typebox";
|
|
6
|
+
var getLabelsContract = {
|
|
7
|
+
method: "GET",
|
|
8
|
+
path: "/cms/labels",
|
|
9
|
+
query: Type.Object({
|
|
10
|
+
section: Type.Optional(Type.String({ description: "\uC139\uC158\uC73C\uB85C \uD544\uD130\uB9C1 (\uC608: home, why-futureplay)" })),
|
|
11
|
+
limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100, default: 20, description: "\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218" })),
|
|
12
|
+
offset: Type.Optional(Type.Number({ minimum: 0, default: 0, description: "\uC2DC\uC791 \uC624\uD504\uC14B" }))
|
|
13
|
+
}),
|
|
14
|
+
response: Type.Object({
|
|
15
|
+
labels: Type.Array(Type.Object({
|
|
16
|
+
id: Type.Number(),
|
|
17
|
+
key: Type.String(),
|
|
18
|
+
section: Type.String(),
|
|
19
|
+
type: Type.String(),
|
|
20
|
+
publishedVersion: Type.Union([Type.Number(), Type.Null()]),
|
|
21
|
+
createdBy: Type.Union([Type.String(), Type.Null()]),
|
|
22
|
+
createdAt: Type.String(),
|
|
23
|
+
updatedAt: Type.String()
|
|
24
|
+
})),
|
|
25
|
+
total: Type.Number(),
|
|
26
|
+
limit: Type.Number(),
|
|
27
|
+
offset: Type.Number()
|
|
28
|
+
})
|
|
29
|
+
};
|
|
30
|
+
var createLabelContract = {
|
|
31
|
+
method: "POST",
|
|
32
|
+
path: "/cms/labels",
|
|
33
|
+
body: Type.Object({
|
|
34
|
+
key: Type.String({
|
|
35
|
+
description: "\uACE0\uC720 \uD0A4 (\uC608: home.hero.title)",
|
|
36
|
+
pattern: "^[a-z0-9-]+\\.[a-z0-9-]+\\.[a-z0-9-]+$"
|
|
37
|
+
}),
|
|
38
|
+
section: Type.String({
|
|
39
|
+
description: "\uC139\uC158 \uC774\uB984 (\uC608: home, why-futureplay)",
|
|
40
|
+
pattern: "^[a-z0-9-]+$"
|
|
41
|
+
}),
|
|
42
|
+
type: Type.Union([
|
|
43
|
+
Type.Literal("text"),
|
|
44
|
+
Type.Literal("image"),
|
|
45
|
+
Type.Literal("video"),
|
|
46
|
+
Type.Literal("file"),
|
|
47
|
+
Type.Literal("object")
|
|
48
|
+
], { description: "\uAC12 \uD0C0\uC785" }),
|
|
49
|
+
createdBy: Type.Optional(Type.String({ description: "\uC0DD\uC131\uC790 ID" }))
|
|
50
|
+
}),
|
|
51
|
+
response: Type.Union([
|
|
52
|
+
Type.Object({
|
|
53
|
+
id: Type.Number(),
|
|
54
|
+
key: Type.String(),
|
|
55
|
+
section: Type.String(),
|
|
56
|
+
type: Type.String(),
|
|
57
|
+
publishedVersion: Type.Union([Type.Number(), Type.Null()]),
|
|
58
|
+
createdBy: Type.Union([Type.String(), Type.Null()]),
|
|
59
|
+
createdAt: Type.String(),
|
|
60
|
+
updatedAt: Type.String()
|
|
61
|
+
}),
|
|
62
|
+
Type.Object({
|
|
63
|
+
error: Type.String(),
|
|
64
|
+
key: Type.Optional(Type.String())
|
|
65
|
+
})
|
|
66
|
+
])
|
|
67
|
+
};
|
|
68
|
+
var getLabelContract = {
|
|
69
|
+
method: "GET",
|
|
70
|
+
path: "/cms/labels/:id",
|
|
71
|
+
params: Type.Object({
|
|
72
|
+
id: Type.String({ description: "\uB77C\uBCA8 ID" })
|
|
73
|
+
}),
|
|
74
|
+
response: Type.Union([
|
|
75
|
+
Type.Object({
|
|
76
|
+
id: Type.Number(),
|
|
77
|
+
key: Type.String(),
|
|
78
|
+
section: Type.String(),
|
|
79
|
+
type: Type.String(),
|
|
80
|
+
publishedVersion: Type.Union([Type.Number(), Type.Null()]),
|
|
81
|
+
createdBy: Type.Union([Type.String(), Type.Null()]),
|
|
82
|
+
createdAt: Type.String(),
|
|
83
|
+
updatedAt: Type.String()
|
|
84
|
+
}),
|
|
85
|
+
Type.Object({
|
|
86
|
+
error: Type.String()
|
|
87
|
+
})
|
|
88
|
+
])
|
|
89
|
+
};
|
|
90
|
+
var updateLabelContract = {
|
|
91
|
+
method: "PATCH",
|
|
92
|
+
path: "/cms/labels/:id",
|
|
93
|
+
params: Type.Object({
|
|
94
|
+
id: Type.String({ description: "\uB77C\uBCA8 ID" })
|
|
95
|
+
}),
|
|
96
|
+
body: Type.Object({
|
|
97
|
+
section: Type.Optional(Type.String({ description: "\uC139\uC158 \uBCC0\uACBD" })),
|
|
98
|
+
type: Type.Optional(Type.Union([
|
|
99
|
+
Type.Literal("text"),
|
|
100
|
+
Type.Literal("image"),
|
|
101
|
+
Type.Literal("video"),
|
|
102
|
+
Type.Literal("file"),
|
|
103
|
+
Type.Literal("object")
|
|
104
|
+
]))
|
|
105
|
+
}),
|
|
106
|
+
response: Type.Union([
|
|
107
|
+
Type.Object({
|
|
108
|
+
id: Type.Number(),
|
|
109
|
+
key: Type.String(),
|
|
110
|
+
section: Type.String(),
|
|
111
|
+
type: Type.String(),
|
|
112
|
+
publishedVersion: Type.Union([Type.Number(), Type.Null()]),
|
|
113
|
+
createdBy: Type.Union([Type.String(), Type.Null()]),
|
|
114
|
+
createdAt: Type.String(),
|
|
115
|
+
updatedAt: Type.String()
|
|
116
|
+
}),
|
|
117
|
+
Type.Object({
|
|
118
|
+
error: Type.String()
|
|
119
|
+
})
|
|
120
|
+
])
|
|
121
|
+
};
|
|
122
|
+
var deleteLabelContract = {
|
|
123
|
+
method: "DELETE",
|
|
124
|
+
path: "/cms/labels/:id",
|
|
125
|
+
params: Type.Object({
|
|
126
|
+
id: Type.String({ description: "\uB77C\uBCA8 ID" })
|
|
127
|
+
}),
|
|
128
|
+
response: Type.Union([
|
|
129
|
+
Type.Object({
|
|
130
|
+
success: Type.Boolean(),
|
|
131
|
+
id: Type.Number()
|
|
132
|
+
}),
|
|
133
|
+
Type.Object({
|
|
134
|
+
error: Type.String()
|
|
135
|
+
})
|
|
136
|
+
])
|
|
137
|
+
};
|
|
138
|
+
var getLabelByKeyContract = {
|
|
139
|
+
method: "GET",
|
|
140
|
+
path: "/cms/labels/by-key/:key",
|
|
141
|
+
params: Type.Object({
|
|
142
|
+
key: Type.String({ description: "\uB77C\uBCA8 Key (\uC608: home.hero.title)" })
|
|
143
|
+
}),
|
|
144
|
+
response: Type.Union([
|
|
145
|
+
Type.Object({
|
|
146
|
+
id: Type.Number(),
|
|
147
|
+
key: Type.String(),
|
|
148
|
+
section: Type.String(),
|
|
149
|
+
type: Type.String(),
|
|
150
|
+
publishedVersion: Type.Union([Type.Number(), Type.Null()]),
|
|
151
|
+
createdBy: Type.Union([Type.String(), Type.Null()]),
|
|
152
|
+
createdAt: Type.String(),
|
|
153
|
+
updatedAt: Type.String()
|
|
154
|
+
}),
|
|
155
|
+
Type.Object({
|
|
156
|
+
error: Type.String(),
|
|
157
|
+
key: Type.Optional(Type.String())
|
|
158
|
+
})
|
|
159
|
+
])
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// src/contracts/published-cache.ts
|
|
163
|
+
import { Type as Type2 } from "@sinclair/typebox";
|
|
164
|
+
var SectionData = Type2.Object({
|
|
165
|
+
section: Type2.String(),
|
|
166
|
+
locale: Type2.String(),
|
|
167
|
+
content: Type2.Record(Type2.String(), Type2.Any()),
|
|
168
|
+
version: Type2.Number(),
|
|
169
|
+
publishedAt: Type2.Union([Type2.String(), Type2.Null()])
|
|
170
|
+
});
|
|
171
|
+
var getPublishedCacheContract = {
|
|
172
|
+
method: "GET",
|
|
173
|
+
path: "/cms/published-cache",
|
|
174
|
+
query: Type2.Object({
|
|
175
|
+
sections: Type2.Union([
|
|
176
|
+
Type2.String({ description: "\uB2E8\uC77C \uC139\uC158 \uC774\uB984 (\uC608: home)" }),
|
|
177
|
+
Type2.Array(Type2.String(), { description: '\uC5EC\uB7EC \uC139\uC158 \uC774\uB984 (\uC608: ["home", "footer"])' })
|
|
178
|
+
]),
|
|
179
|
+
locale: Type2.Optional(Type2.String({ default: "ko", description: "\uC5B8\uC5B4 \uCF54\uB4DC" }))
|
|
180
|
+
}),
|
|
181
|
+
response: Type2.Union([
|
|
182
|
+
// 성공: 항상 배열로 반환
|
|
183
|
+
Type2.Array(SectionData),
|
|
184
|
+
// 에러
|
|
185
|
+
Type2.Object({
|
|
186
|
+
error: Type2.String()
|
|
187
|
+
})
|
|
188
|
+
])
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/store.ts
|
|
192
|
+
import React from "react";
|
|
193
|
+
import { create } from "zustand";
|
|
194
|
+
function replaceVariables(text, replace) {
|
|
195
|
+
return text.replace(/\{(\w+)}/g, (match, key) => {
|
|
196
|
+
const value = replace[key];
|
|
197
|
+
return value !== void 0 ? String(value) : match;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
var useCmsStore = create((set, get) => ({
|
|
201
|
+
sections: {},
|
|
202
|
+
loading: {},
|
|
203
|
+
setSection: (section, data) => {
|
|
204
|
+
set((state) => ({
|
|
205
|
+
sections: {
|
|
206
|
+
...state.sections,
|
|
207
|
+
[section]: data
|
|
208
|
+
}
|
|
209
|
+
}));
|
|
210
|
+
},
|
|
211
|
+
setSections: (sections) => {
|
|
212
|
+
set((state) => ({
|
|
213
|
+
sections: {
|
|
214
|
+
...state.sections,
|
|
215
|
+
...sections
|
|
216
|
+
}
|
|
217
|
+
}));
|
|
218
|
+
},
|
|
219
|
+
loadSection: async (section, locale = "ko") => {
|
|
220
|
+
const state = get();
|
|
221
|
+
if (state.loading[section]) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (state.sections[section]) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
set((state2) => ({
|
|
228
|
+
loading: { ...state2.loading, [section]: true }
|
|
229
|
+
}));
|
|
230
|
+
try {
|
|
231
|
+
const response = await cmsApi.publishedCache.get({
|
|
232
|
+
query: { sections: section, locale }
|
|
233
|
+
});
|
|
234
|
+
if ("error" in response) {
|
|
235
|
+
console.error(`Failed to load section ${section}:`, response.error);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const data = response[0];
|
|
239
|
+
if (!data) {
|
|
240
|
+
console.warn(`Section ${section} not found`);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const sectionData = {
|
|
244
|
+
section: data.section,
|
|
245
|
+
locale: data.locale,
|
|
246
|
+
content: data.content || {},
|
|
247
|
+
version: data.version || 0,
|
|
248
|
+
publishedAt: data.publishedAt || null
|
|
249
|
+
};
|
|
250
|
+
set((state2) => ({
|
|
251
|
+
sections: {
|
|
252
|
+
...state2.sections,
|
|
253
|
+
[section]: sectionData
|
|
254
|
+
},
|
|
255
|
+
loading: { ...state2.loading, [section]: false }
|
|
256
|
+
}));
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error(`Error loading CMS section ${section}:`, error);
|
|
259
|
+
set((state2) => ({
|
|
260
|
+
loading: { ...state2.loading, [section]: false }
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
updateLabel: (section, key, value) => {
|
|
265
|
+
set((state) => ({
|
|
266
|
+
sections: {
|
|
267
|
+
...state.sections,
|
|
268
|
+
[section]: {
|
|
269
|
+
...state.sections[section],
|
|
270
|
+
content: {
|
|
271
|
+
...state.sections[section]?.content,
|
|
272
|
+
[`${section}.${key}`]: value
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}));
|
|
277
|
+
},
|
|
278
|
+
reset: () => {
|
|
279
|
+
set({
|
|
280
|
+
sections: {},
|
|
281
|
+
loading: {}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}));
|
|
285
|
+
function useSection(section, options = {}) {
|
|
286
|
+
const { autoLoad = false, locale = "ko" } = options;
|
|
287
|
+
const sectionData = useCmsStore((state) => state.sections[section]);
|
|
288
|
+
const loading = useCmsStore((state) => state.loading[section] ?? false);
|
|
289
|
+
const loadSection = useCmsStore((state) => state.loadSection);
|
|
290
|
+
React.useEffect(() => {
|
|
291
|
+
if (autoLoad && !sectionData && !loading) {
|
|
292
|
+
loadSection(section, locale);
|
|
293
|
+
}
|
|
294
|
+
}, [autoLoad, section, locale, sectionData, loading, loadSection]);
|
|
295
|
+
const t = React.useCallback(
|
|
296
|
+
(key, defaultValue, replace) => {
|
|
297
|
+
if (!sectionData) {
|
|
298
|
+
return defaultValue;
|
|
299
|
+
}
|
|
300
|
+
const fullKey = `${section}.${key}`;
|
|
301
|
+
let value = sectionData.content[fullKey];
|
|
302
|
+
if (value === void 0) {
|
|
303
|
+
value = defaultValue;
|
|
304
|
+
}
|
|
305
|
+
if (typeof value === "string" && replace) {
|
|
306
|
+
value = replaceVariables(value, replace);
|
|
307
|
+
}
|
|
308
|
+
return value;
|
|
309
|
+
},
|
|
310
|
+
[section, sectionData]
|
|
311
|
+
);
|
|
312
|
+
return {
|
|
313
|
+
t,
|
|
314
|
+
data: sectionData,
|
|
315
|
+
loading
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function useSections(sectionNames) {
|
|
319
|
+
const allSections = useCmsStore((state) => state.sections);
|
|
320
|
+
const allLoading = useCmsStore((state) => state.loading);
|
|
321
|
+
const result = {};
|
|
322
|
+
sectionNames.forEach((section) => {
|
|
323
|
+
const sectionData = allSections[section];
|
|
324
|
+
const loading = allLoading[section] ?? false;
|
|
325
|
+
const t = (key, defaultValue, replace) => {
|
|
326
|
+
if (!sectionData) {
|
|
327
|
+
return defaultValue;
|
|
328
|
+
}
|
|
329
|
+
const fullKey = `${section}.${key}`;
|
|
330
|
+
let value = sectionData.content[fullKey];
|
|
331
|
+
if (value === void 0) {
|
|
332
|
+
value = defaultValue;
|
|
333
|
+
}
|
|
334
|
+
if (typeof value === "string" && replace) {
|
|
335
|
+
value = replaceVariables(value, replace);
|
|
336
|
+
}
|
|
337
|
+
return value;
|
|
338
|
+
};
|
|
339
|
+
result[section] = {
|
|
340
|
+
t,
|
|
341
|
+
data: sectionData,
|
|
342
|
+
loading
|
|
343
|
+
};
|
|
344
|
+
});
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/init.tsx
|
|
349
|
+
import { useEffect } from "react";
|
|
350
|
+
function InitCms({ sections }) {
|
|
351
|
+
const setSections = useCmsStore((state) => state.setSections);
|
|
352
|
+
useEffect(() => {
|
|
353
|
+
const sectionsData = {};
|
|
354
|
+
Object.entries(sections).forEach(([key, { data }]) => {
|
|
355
|
+
sectionsData[key] = data;
|
|
356
|
+
});
|
|
357
|
+
setSections(sectionsData);
|
|
358
|
+
}, [sections, setSections]);
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/client.ts
|
|
363
|
+
var cmsApi = {
|
|
364
|
+
/**
|
|
365
|
+
* Labels API
|
|
366
|
+
*/
|
|
367
|
+
labels: {
|
|
17
368
|
/**
|
|
18
|
-
*
|
|
369
|
+
* GET /cms/labels
|
|
370
|
+
* 라벨 목록 조회 (섹션 필터, 페이지네이션)
|
|
19
371
|
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* GET /cms/labels
|
|
23
|
-
* 라벨 목록 조회 (섹션 필터, 페이지네이션)
|
|
24
|
-
*/
|
|
25
|
-
list: (options) => client.call('/cms/labels', getLabelsContract, options),
|
|
26
|
-
/**
|
|
27
|
-
* GET /cms/labels/:id
|
|
28
|
-
* 특정 라벨 조회
|
|
29
|
-
*/
|
|
30
|
-
getById: (options) => client.call('/cms/labels/:id', getLabelContract, options),
|
|
31
|
-
/**
|
|
32
|
-
* POST /cms/labels
|
|
33
|
-
* 새 라벨 생성
|
|
34
|
-
*/
|
|
35
|
-
create: (options) => client.call('/cms/labels', createLabelContract, options),
|
|
36
|
-
/**
|
|
37
|
-
* PATCH /cms/labels/:id
|
|
38
|
-
* 라벨 업데이트
|
|
39
|
-
*/
|
|
40
|
-
update: (options) => client.call('/cms/labels/:id', updateLabelContract, options),
|
|
41
|
-
/**
|
|
42
|
-
* DELETE /cms/labels/:id
|
|
43
|
-
* 라벨 삭제
|
|
44
|
-
*/
|
|
45
|
-
delete: (options) => client.call('/cms/labels/:id', deleteLabelContract, options),
|
|
46
|
-
},
|
|
372
|
+
list: (options) => client.call(getLabelsContract, options),
|
|
47
373
|
/**
|
|
48
|
-
*
|
|
374
|
+
* GET /cms/labels/:id
|
|
375
|
+
* 특정 라벨 조회
|
|
49
376
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
377
|
+
getById: (options) => client.call(getLabelContract, options),
|
|
378
|
+
/**
|
|
379
|
+
* POST /cms/labels
|
|
380
|
+
* 새 라벨 생성
|
|
381
|
+
*/
|
|
382
|
+
create: (options) => client.call(createLabelContract, options),
|
|
383
|
+
/**
|
|
384
|
+
* PATCH /cms/labels/:id
|
|
385
|
+
* 라벨 업데이트
|
|
386
|
+
*/
|
|
387
|
+
update: (options) => client.call(updateLabelContract, options),
|
|
388
|
+
/**
|
|
389
|
+
* DELETE /cms/labels/:id
|
|
390
|
+
* 라벨 삭제
|
|
391
|
+
*/
|
|
392
|
+
delete: (options) => client.call(deleteLabelContract, options)
|
|
393
|
+
},
|
|
394
|
+
/**
|
|
395
|
+
* Published Cache API
|
|
396
|
+
*/
|
|
397
|
+
publishedCache: {
|
|
398
|
+
/**
|
|
399
|
+
* GET /cms/published-cache
|
|
400
|
+
* 발행된 콘텐츠 캐시 조회
|
|
401
|
+
*/
|
|
402
|
+
get: (options) => client.call(getPublishedCacheContract, options)
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
export {
|
|
406
|
+
InitCms,
|
|
407
|
+
cmsApi,
|
|
408
|
+
useCmsStore,
|
|
409
|
+
useSection,
|
|
410
|
+
useSections
|
|
57
411
|
};
|
|
58
|
-
// Client-side Store & Hooks
|
|
59
|
-
export { useCmsStore, useSection, useSections } from './store';
|
|
60
|
-
// Client-side Initializer
|
|
61
|
-
export { InitCms } from './init';
|
|
62
412
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,SAAS;AACT,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACH,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACtB,MAAM,+BAA+B,CAAC;AAEvC,kBAAkB;AAClB,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAE9E;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IAClB;;OAEG;IACH,MAAM,EAAE;QACJ;;;WAGG;QACH,IAAI,EAAE,CAAC,OAAsE,EAAE,EAAE,CAC7E,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,iBAAiB,EAAE,OAAO,CAAC;QAE1D;;;WAGG;QACH,OAAO,EAAE,CAAC,OAAqE,EAAE,EAAE,CAC/E,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,EAAE,OAAO,CAAC;QAE7D;;;WAGG;QACH,MAAM,EAAE,CAAC,OAAoE,EAAE,EAAE,CAC7E,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,mBAAmB,EAAE,OAAO,CAAC;QAE5D;;;WAGG;QACH,MAAM,EAAE,CAAC,OAGR,EAAE,EAAE,CACD,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,mBAAmB,EAAE,OAAO,CAAC;QAEhE;;;WAGG;QACH,MAAM,EAAE,CAAC,OAAwE,EAAE,EAAE,CACjF,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,mBAAmB,EAAE,OAAO,CAAC;KACnE;IAED;;OAEG;IACH,cAAc,EAAE;QACZ;;;WAGG;QACH,GAAG,EAAE,CAAC,OAA4E,EAAE,EAAE,CAClF,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,yBAAyB,EAAE,OAAO,CAAC;KAC9E;CACK,CAAC;AAOX,4BAA4B;AAC5B,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/D,0BAA0B;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/contracts/labels.ts","../src/contracts/published-cache.ts","../src/store.ts","../src/init.tsx"],"sourcesContent":["/**\n * @spfn/cms/client\n *\n * Client Components Only\n * 클라이언트 컴포넌트 전용 (브라우저에서 실행)\n */\n\nimport { client } from '@spfn/core/client';\nimport type { InferContract } from '@spfn/core';\n\n// Labels\nimport {\n getLabelsContract,\n createLabelContract,\n getLabelContract,\n updateLabelContract,\n deleteLabelContract\n} from './contracts/labels';\n\n// Published Cache\nimport { getPublishedCacheContract } from './contracts/published-cache';\n\n/**\n * CMS API Client\n */\nexport const cmsApi = {\n /**\n * Labels API\n */\n labels: {\n /**\n * GET /cms/labels\n * 라벨 목록 조회 (섹션 필터, 페이지네이션)\n */\n list: (options?: { query?: InferContract<typeof getLabelsContract>['query'] }) =>\n client.call(getLabelsContract, options),\n\n /**\n * GET /cms/labels/:id\n * 특정 라벨 조회\n */\n getById: (options: { params: InferContract<typeof getLabelContract>['params'] }) =>\n client.call(getLabelContract, options),\n\n /**\n * POST /cms/labels\n * 새 라벨 생성\n */\n create: (options: { body: InferContract<typeof createLabelContract>['body'] }) =>\n client.call(createLabelContract, options),\n\n /**\n * PATCH /cms/labels/:id\n * 라벨 업데이트\n */\n update: (options: {\n params: InferContract<typeof updateLabelContract>['params'];\n body: InferContract<typeof updateLabelContract>['body'];\n }) =>\n client.call(updateLabelContract, options),\n\n /**\n * DELETE /cms/labels/:id\n * 라벨 삭제\n */\n delete: (options: { params: InferContract<typeof deleteLabelContract>['params'] }) =>\n client.call(deleteLabelContract, options),\n },\n\n /**\n * Published Cache API\n */\n publishedCache: {\n /**\n * GET /cms/published-cache\n * 발행된 콘텐츠 캐시 조회\n */\n get: (options: { query: InferContract<typeof getPublishedCacheContract>['query'] }) =>\n client.call(getPublishedCacheContract, options),\n },\n} as const;\n\n/**\n * Type exports\n */\nexport type CmsApi = typeof cmsApi;\n\n// Client-side Store & Hooks\nexport { useCmsStore, useSection, useSections } from './store';\n\n// Client-side Initializer\nexport { InitCms } from './init';","import { Type } from '@sinclair/typebox';\nimport type { RouteContract } from '@spfn/core/route';\n\n/**\n * CMS Labels Contracts\n *\n * 라벨 메타데이터 관리 API\n */\n\n/**\n * GET /cms/labels - 라벨 목록 조회\n */\nexport const getLabelsContract = {\n method: 'GET' as const,\n path: '/cms/labels',\n query: Type.Object({\n section: Type.Optional(Type.String({ description: '섹션으로 필터링 (예: home, why-futureplay)' })),\n limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100, default: 20, description: '페이지당 항목 수' })),\n offset: Type.Optional(Type.Number({ minimum: 0, default: 0, description: '시작 오프셋' }))\n }),\n response: Type.Object({\n labels: Type.Array(Type.Object({\n id: Type.Number(),\n key: Type.String(),\n section: Type.String(),\n type: Type.String(),\n publishedVersion: Type.Union([Type.Number(), Type.Null()]),\n createdBy: Type.Union([Type.String(), Type.Null()]),\n createdAt: Type.String(),\n updatedAt: Type.String()\n })),\n total: Type.Number(),\n limit: Type.Number(),\n offset: Type.Number()\n })\n} as const satisfies RouteContract;\n\n/**\n * POST /cms/labels - 새 라벨 생성\n */\nexport const createLabelContract = {\n method: 'POST' as const,\n path: '/cms/labels',\n body: Type.Object({\n key: Type.String({\n description: '고유 키 (예: home.hero.title)',\n pattern: '^[a-z0-9-]+\\\\.[a-z0-9-]+\\\\.[a-z0-9-]+$'\n }),\n section: Type.String({\n description: '섹션 이름 (예: home, why-futureplay)',\n pattern: '^[a-z0-9-]+$'\n }),\n type: Type.Union([\n Type.Literal('text'),\n Type.Literal('image'),\n Type.Literal('video'),\n Type.Literal('file'),\n Type.Literal('object')\n ], { description: '값 타입' }),\n createdBy: Type.Optional(Type.String({ description: '생성자 ID' }))\n }),\n response: Type.Union([\n Type.Object({\n id: Type.Number(),\n key: Type.String(),\n section: Type.String(),\n type: Type.String(),\n publishedVersion: Type.Union([Type.Number(), Type.Null()]),\n createdBy: Type.Union([Type.String(), Type.Null()]),\n createdAt: Type.String(),\n updatedAt: Type.String()\n }),\n Type.Object({\n error: Type.String(),\n key: Type.Optional(Type.String())\n })\n ])\n} as const satisfies RouteContract;\n\n/**\n * GET /cms/labels/:id - 라벨 단건 조회\n */\nexport const getLabelContract = {\n method: 'GET' as const,\n path: '/cms/labels/:id',\n params: Type.Object({\n id: Type.String({ description: '라벨 ID' })\n }),\n response: Type.Union([\n Type.Object({\n id: Type.Number(),\n key: Type.String(),\n section: Type.String(),\n type: Type.String(),\n publishedVersion: Type.Union([Type.Number(), Type.Null()]),\n createdBy: Type.Union([Type.String(), Type.Null()]),\n createdAt: Type.String(),\n updatedAt: Type.String()\n }),\n Type.Object({\n error: Type.String()\n })\n ])\n} as const satisfies RouteContract;\n\n/**\n * PATCH /cms/labels/:id - 라벨 메타데이터 수정\n */\nexport const updateLabelContract = {\n method: 'PATCH' as const,\n path: '/cms/labels/:id',\n params: Type.Object({\n id: Type.String({ description: '라벨 ID' })\n }),\n body: Type.Object({\n section: Type.Optional(Type.String({ description: '섹션 변경' })),\n type: Type.Optional(Type.Union([\n Type.Literal('text'),\n Type.Literal('image'),\n Type.Literal('video'),\n Type.Literal('file'),\n Type.Literal('object')\n ]))\n }),\n response: Type.Union([\n Type.Object({\n id: Type.Number(),\n key: Type.String(),\n section: Type.String(),\n type: Type.String(),\n publishedVersion: Type.Union([Type.Number(), Type.Null()]),\n createdBy: Type.Union([Type.String(), Type.Null()]),\n createdAt: Type.String(),\n updatedAt: Type.String()\n }),\n Type.Object({\n error: Type.String()\n })\n ])\n} as const satisfies RouteContract;\n\n/**\n * DELETE /cms/labels/:id - 라벨 삭제\n */\nexport const deleteLabelContract = {\n method: 'DELETE' as const,\n path: '/cms/labels/:id',\n params: Type.Object({\n id: Type.String({ description: '라벨 ID' })\n }),\n response: Type.Union([\n Type.Object({\n success: Type.Boolean(),\n id: Type.Number()\n }),\n Type.Object({\n error: Type.String()\n })\n ])\n} as const satisfies RouteContract;\n\n/**\n * GET /cms/labels/by-key/:key - Key로 라벨 조회\n */\nexport const getLabelByKeyContract = {\n method: 'GET' as const,\n path: '/cms/labels/by-key/:key',\n params: Type.Object({\n key: Type.String({ description: '라벨 Key (예: home.hero.title)' })\n }),\n response: Type.Union([\n Type.Object({\n id: Type.Number(),\n key: Type.String(),\n section: Type.String(),\n type: Type.String(),\n publishedVersion: Type.Union([Type.Number(), Type.Null()]),\n createdBy: Type.Union([Type.String(), Type.Null()]),\n createdAt: Type.String(),\n updatedAt: Type.String()\n }),\n Type.Object({\n error: Type.String(),\n key: Type.Optional(Type.String())\n })\n ])\n} as const satisfies RouteContract;","import { Type } from '@sinclair/typebox';\nimport type { RouteContract } from '@spfn/core/route';\n\nconst SectionData = Type.Object({\n section: Type.String(),\n locale: Type.String(),\n content: Type.Record(Type.String(), Type.Any()),\n version: Type.Number(),\n publishedAt: Type.Union([Type.String(), Type.Null()]),\n});\n\n/**\n * GET /cms/published-cache\n * 발행된 콘텐츠 캐시 조회 (단일 또는 여러 섹션)\n */\nexport const getPublishedCacheContract = {\n method: 'GET' as const,\n path: '/cms/published-cache',\n query: Type.Object({\n sections: Type.Union([\n Type.String({ description: '단일 섹션 이름 (예: home)' }),\n Type.Array(Type.String(), { description: '여러 섹션 이름 (예: [\"home\", \"footer\"])' })\n ]),\n locale: Type.Optional(Type.String({ default: 'ko', description: '언어 코드' })),\n }),\n response: Type.Union([\n // 성공: 항상 배열로 반환\n Type.Array(SectionData),\n // 에러\n Type.Object({\n error: Type.String()\n })\n ])\n} as const satisfies RouteContract;","/**\n * CMS Zustand Store\n *\n * 클라이언트 컴포넌트에서 CMS 사용을 위한 상태 관리\n * - 서버에서 초기화된 데이터를 클라이언트로 전달\n * - 클라이언트에서 비동기로 로드 가능\n * - 서버 API와 동일한 패턴 ({ t } 사용)\n */\n\n'use client';\n\nimport React from 'react';\nimport { create } from 'zustand';\nimport { cmsApi } from './client';\nimport type { SectionData } from './server';\n\n/**\n * 변수 치환 헬퍼 (서버와 동일)\n */\nfunction replaceVariables(text: string, replace: Record<string, string | number>): string\n{\n return text.replace(/\\{(\\w+)}/g, (match, key) =>\n {\n const value = replace[key];\n return value !== undefined ? String(value) : match;\n });\n}\n\ninterface CmsState\n{\n /**\n * 섹션별 데이터\n * { 'home': { section: 'home', content: {...}, version: 1, ... }, ... }\n */\n sections: Record<string, SectionData>;\n\n /**\n * 로딩 상태\n */\n loading: Record<string, boolean>;\n\n /**\n * 섹션 데이터 설정 (서버에서 초기화용)\n */\n setSection: (section: string, data: SectionData) => void;\n\n /**\n * 여러 섹션 한번에 설정\n */\n setSections: (sections: Record<string, SectionData>) => void;\n\n /**\n * 섹션 비동기 로드\n */\n loadSection: (section: string, locale?: string) => Promise<void>;\n\n /**\n * 라벨 업데이트 (Draft Mode용)\n */\n updateLabel: (section: string, key: string, value: any) => void;\n\n /**\n * 초기화\n */\n reset: () => void;\n}\n\nexport const useCmsStore = create<CmsState>((set, get) => ({\n sections: {},\n loading: {},\n\n setSection: (section, data) =>\n {\n set((state) => ({\n sections: {\n ...state.sections,\n [section]: data,\n },\n }));\n },\n\n setSections: (sections) =>\n {\n set((state) => ({\n sections: {\n ...state.sections,\n ...sections,\n },\n }));\n },\n\n loadSection: async (section, locale = 'ko') =>\n {\n const state = get();\n\n // 이미 로드 중이면 스킵\n if (state.loading[section])\n {\n return;\n }\n\n // 이미 로드되어 있으면 스킵\n if (state.sections[section])\n {\n return;\n }\n\n set((state) => ({\n loading: { ...state.loading, [section]: true },\n }));\n\n try\n {\n const response = await cmsApi.publishedCache.get({\n query: { sections: section, locale },\n });\n\n // Check for error response\n if ('error' in response)\n {\n console.error(`Failed to load section ${section}:`, response.error);\n return;\n }\n\n // Response is array, get first element\n const data = response[0];\n\n if (!data)\n {\n console.warn(`Section ${section} not found`);\n return;\n }\n\n const sectionData: SectionData = {\n section: data.section,\n locale: data.locale,\n content: (data.content as Record<string, any>) || {},\n version: data.version || 0,\n publishedAt: data.publishedAt || null,\n };\n\n set((state) => ({\n sections: {\n ...state.sections,\n [section]: sectionData,\n },\n loading: { ...state.loading, [section]: false },\n }));\n }\n catch (error)\n {\n console.error(`Error loading CMS section ${section}:`, error);\n set((state) => ({\n loading: { ...state.loading, [section]: false },\n }));\n }\n },\n\n updateLabel: (section, key, value) =>\n {\n set((state) => ({\n sections: {\n ...state.sections,\n [section]: {\n ...state.sections[section],\n content: {\n ...state.sections[section]?.content,\n [`${section}.${key}`]: value,\n },\n },\n },\n }));\n },\n\n reset: () =>\n {\n set({\n sections: {},\n loading: {},\n });\n },\n}));\n\n/**\n * 섹션 Hook (서버 API와 동일한 패턴)\n *\n * @param section - 섹션 이름\n * @param options - 옵션 (autoLoad: 자동 로드 여부)\n * @returns { t, data, loading }\n *\n * @example\n * ```tsx\n * 'use client';\n * import { useSection } from '@/lib/cms';\n *\n * export function ClientComponent()\n * {\n * const { t } = useSection('home');\n * return <h1>{t('hero.title')}</h1>;\n * }\n * ```\n */\nexport function useSection(\n section: string,\n options: { autoLoad?: boolean; locale?: string } = {}\n)\n{\n const { autoLoad = false, locale = 'ko' } = options;\n\n const sectionData = useCmsStore((state) => state.sections[section]);\n const loading = useCmsStore((state) => state.loading[section] ?? false);\n const loadSection = useCmsStore((state) => state.loadSection);\n\n // 자동 로드 옵션이 켜져있고 데이터가 없으면 로드\n React.useEffect(() =>\n {\n if (autoLoad && !sectionData && !loading)\n {\n loadSection(section, locale);\n }\n }, [autoLoad, section, locale, sectionData, loading, loadSection]);\n\n // Translation function (서버와 동일)\n const t = React.useCallback(\n (key: string, defaultValue?: any, replace?: Record<string, string | number>) =>\n {\n if (!sectionData)\n {\n return defaultValue;\n }\n\n const fullKey = `${section}.${key}`;\n let value = sectionData.content[fullKey];\n\n if (value === undefined)\n {\n value = defaultValue;\n }\n\n // 문자열이고 치환 맵이 있으면 변수 치환\n if (typeof value === 'string' && replace)\n {\n value = replaceVariables(value, replace);\n }\n\n return value;\n },\n [section, sectionData]\n );\n\n return {\n t,\n data: sectionData,\n loading,\n };\n}\n\n/**\n * 여러 섹션 Hook\n *\n * @param sectionNames - 섹션 이름 배열\n * @returns { [section]: { t, data, loading }, ... }\n *\n * @example\n * ```tsx\n * const sections = useSections(['home', 'why-futureplay']);\n * sections.home.t('hero.title');\n * ```\n */\nexport function useSections(sectionNames: string[])\n{\n const allSections = useCmsStore((state) => state.sections);\n const allLoading = useCmsStore((state) => state.loading);\n\n const result: Record<string, ReturnType<typeof useSection>> = {};\n\n sectionNames.forEach((section) =>\n {\n const sectionData = allSections[section];\n const loading = allLoading[section] ?? false;\n\n const t = (key: string, defaultValue?: any, replace?: Record<string, string | number>) =>\n {\n if (!sectionData)\n {\n return defaultValue;\n }\n\n const fullKey = `${section}.${key}`;\n let value = sectionData.content[fullKey];\n\n if (value === undefined)\n {\n value = defaultValue;\n }\n\n if (typeof value === 'string' && replace)\n {\n value = replaceVariables(value, replace);\n }\n\n return value;\n };\n\n result[section] = {\n t,\n data: sectionData,\n loading,\n };\n });\n\n return result;\n}","/**\n * CMS Store Initializer\n *\n * 서버 컴포넌트에서 로드한 섹션 데이터를 클라이언트 store에 초기화\n */\n\n'use client';\n\nimport { useEffect } from 'react';\nimport { useCmsStore } from './store';\nimport type { SectionAPI } from './server';\n\ninterface InitCmsProps\n{\n /**\n * 서버에서 로드한 섹션 데이터\n * { home: { t, data }, ... }\n */\n sections: Record<string, SectionAPI>;\n}\n\n/**\n * CMS Store 초기화 컴포넌트\n *\n * 서버 컴포넌트에서 로드한 데이터를 클라이언트 store에 주입\n * 이후 하위 클라이언트 컴포넌트에서 useSection() 사용 가능\n *\n * @param props - InitCmsProps\n * @param props.sections - 서버에서 로드한 섹션 데이터\n *\n * @example\n * // Server Component\n * const home = await getSection('home');\n * <InitCms sections={{ home }} />\n * // 이후 하위 클라이언트 컴포넌트에서 useSection('home') 사용 가능\n */\nexport function InitCms({ sections }: InitCmsProps)\n{\n const setSections = useCmsStore((state) => state.setSections);\n\n useEffect(() =>\n {\n // SectionAPI → SectionData 변환\n const sectionsData: Record<string, any> = {};\n Object.entries(sections).forEach(([key, { data }]) =>\n {\n sectionsData[key] = data;\n });\n\n setSections(sectionsData);\n }, [sections, setSections]);\n\n return null;\n}"],"mappings":"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","names":["Type","state"]}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CMS Labels Contracts
|
|
5
|
+
*
|
|
6
|
+
* 라벨 메타데이터 관리 API
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* GET /cms/labels - 라벨 목록 조회
|
|
10
|
+
*/
|
|
11
|
+
declare const getLabelsContract: {
|
|
12
|
+
readonly method: "GET";
|
|
13
|
+
readonly path: "/cms/labels";
|
|
14
|
+
readonly query: _sinclair_typebox.TObject<{
|
|
15
|
+
section: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
16
|
+
limit: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
17
|
+
offset: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
18
|
+
}>;
|
|
19
|
+
readonly response: _sinclair_typebox.TObject<{
|
|
20
|
+
labels: _sinclair_typebox.TArray<_sinclair_typebox.TObject<{
|
|
21
|
+
id: _sinclair_typebox.TNumber;
|
|
22
|
+
key: _sinclair_typebox.TString;
|
|
23
|
+
section: _sinclair_typebox.TString;
|
|
24
|
+
type: _sinclair_typebox.TString;
|
|
25
|
+
publishedVersion: _sinclair_typebox.TUnion<[_sinclair_typebox.TNumber, _sinclair_typebox.TNull]>;
|
|
26
|
+
createdBy: _sinclair_typebox.TUnion<[_sinclair_typebox.TString, _sinclair_typebox.TNull]>;
|
|
27
|
+
createdAt: _sinclair_typebox.TString;
|
|
28
|
+
updatedAt: _sinclair_typebox.TString;
|
|
29
|
+
}>>;
|
|
30
|
+
total: _sinclair_typebox.TNumber;
|
|
31
|
+
limit: _sinclair_typebox.TNumber;
|
|
32
|
+
offset: _sinclair_typebox.TNumber;
|
|
33
|
+
}>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* POST /cms/labels - 새 라벨 생성
|
|
37
|
+
*/
|
|
38
|
+
declare const createLabelContract: {
|
|
39
|
+
readonly method: "POST";
|
|
40
|
+
readonly path: "/cms/labels";
|
|
41
|
+
readonly body: _sinclair_typebox.TObject<{
|
|
42
|
+
key: _sinclair_typebox.TString;
|
|
43
|
+
section: _sinclair_typebox.TString;
|
|
44
|
+
type: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"text">, _sinclair_typebox.TLiteral<"image">, _sinclair_typebox.TLiteral<"video">, _sinclair_typebox.TLiteral<"file">, _sinclair_typebox.TLiteral<"object">]>;
|
|
45
|
+
createdBy: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
46
|
+
}>;
|
|
47
|
+
readonly response: _sinclair_typebox.TUnion<[_sinclair_typebox.TObject<{
|
|
48
|
+
id: _sinclair_typebox.TNumber;
|
|
49
|
+
key: _sinclair_typebox.TString;
|
|
50
|
+
section: _sinclair_typebox.TString;
|
|
51
|
+
type: _sinclair_typebox.TString;
|
|
52
|
+
publishedVersion: _sinclair_typebox.TUnion<[_sinclair_typebox.TNumber, _sinclair_typebox.TNull]>;
|
|
53
|
+
createdBy: _sinclair_typebox.TUnion<[_sinclair_typebox.TString, _sinclair_typebox.TNull]>;
|
|
54
|
+
createdAt: _sinclair_typebox.TString;
|
|
55
|
+
updatedAt: _sinclair_typebox.TString;
|
|
56
|
+
}>, _sinclair_typebox.TObject<{
|
|
57
|
+
error: _sinclair_typebox.TString;
|
|
58
|
+
key: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
59
|
+
}>]>;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* GET /cms/labels/:id - 라벨 단건 조회
|
|
63
|
+
*/
|
|
64
|
+
declare const getLabelContract: {
|
|
65
|
+
readonly method: "GET";
|
|
66
|
+
readonly path: "/cms/labels/:id";
|
|
67
|
+
readonly params: _sinclair_typebox.TObject<{
|
|
68
|
+
id: _sinclair_typebox.TString;
|
|
69
|
+
}>;
|
|
70
|
+
readonly response: _sinclair_typebox.TUnion<[_sinclair_typebox.TObject<{
|
|
71
|
+
id: _sinclair_typebox.TNumber;
|
|
72
|
+
key: _sinclair_typebox.TString;
|
|
73
|
+
section: _sinclair_typebox.TString;
|
|
74
|
+
type: _sinclair_typebox.TString;
|
|
75
|
+
publishedVersion: _sinclair_typebox.TUnion<[_sinclair_typebox.TNumber, _sinclair_typebox.TNull]>;
|
|
76
|
+
createdBy: _sinclair_typebox.TUnion<[_sinclair_typebox.TString, _sinclair_typebox.TNull]>;
|
|
77
|
+
createdAt: _sinclair_typebox.TString;
|
|
78
|
+
updatedAt: _sinclair_typebox.TString;
|
|
79
|
+
}>, _sinclair_typebox.TObject<{
|
|
80
|
+
error: _sinclair_typebox.TString;
|
|
81
|
+
}>]>;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* PATCH /cms/labels/:id - 라벨 메타데이터 수정
|
|
85
|
+
*/
|
|
86
|
+
declare const updateLabelContract: {
|
|
87
|
+
readonly method: "PATCH";
|
|
88
|
+
readonly path: "/cms/labels/:id";
|
|
89
|
+
readonly params: _sinclair_typebox.TObject<{
|
|
90
|
+
id: _sinclair_typebox.TString;
|
|
91
|
+
}>;
|
|
92
|
+
readonly body: _sinclair_typebox.TObject<{
|
|
93
|
+
section: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
94
|
+
type: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"text">, _sinclair_typebox.TLiteral<"image">, _sinclair_typebox.TLiteral<"video">, _sinclair_typebox.TLiteral<"file">, _sinclair_typebox.TLiteral<"object">]>>;
|
|
95
|
+
}>;
|
|
96
|
+
readonly response: _sinclair_typebox.TUnion<[_sinclair_typebox.TObject<{
|
|
97
|
+
id: _sinclair_typebox.TNumber;
|
|
98
|
+
key: _sinclair_typebox.TString;
|
|
99
|
+
section: _sinclair_typebox.TString;
|
|
100
|
+
type: _sinclair_typebox.TString;
|
|
101
|
+
publishedVersion: _sinclair_typebox.TUnion<[_sinclair_typebox.TNumber, _sinclair_typebox.TNull]>;
|
|
102
|
+
createdBy: _sinclair_typebox.TUnion<[_sinclair_typebox.TString, _sinclair_typebox.TNull]>;
|
|
103
|
+
createdAt: _sinclair_typebox.TString;
|
|
104
|
+
updatedAt: _sinclair_typebox.TString;
|
|
105
|
+
}>, _sinclair_typebox.TObject<{
|
|
106
|
+
error: _sinclair_typebox.TString;
|
|
107
|
+
}>]>;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* DELETE /cms/labels/:id - 라벨 삭제
|
|
111
|
+
*/
|
|
112
|
+
declare const deleteLabelContract: {
|
|
113
|
+
readonly method: "DELETE";
|
|
114
|
+
readonly path: "/cms/labels/:id";
|
|
115
|
+
readonly params: _sinclair_typebox.TObject<{
|
|
116
|
+
id: _sinclair_typebox.TString;
|
|
117
|
+
}>;
|
|
118
|
+
readonly response: _sinclair_typebox.TUnion<[_sinclair_typebox.TObject<{
|
|
119
|
+
success: _sinclair_typebox.TBoolean;
|
|
120
|
+
id: _sinclair_typebox.TNumber;
|
|
121
|
+
}>, _sinclair_typebox.TObject<{
|
|
122
|
+
error: _sinclair_typebox.TString;
|
|
123
|
+
}>]>;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* GET /cms/labels/by-key/:key - Key로 라벨 조회
|
|
127
|
+
*/
|
|
128
|
+
declare const getLabelByKeyContract: {
|
|
129
|
+
readonly method: "GET";
|
|
130
|
+
readonly path: "/cms/labels/by-key/:key";
|
|
131
|
+
readonly params: _sinclair_typebox.TObject<{
|
|
132
|
+
key: _sinclair_typebox.TString;
|
|
133
|
+
}>;
|
|
134
|
+
readonly response: _sinclair_typebox.TUnion<[_sinclair_typebox.TObject<{
|
|
135
|
+
id: _sinclair_typebox.TNumber;
|
|
136
|
+
key: _sinclair_typebox.TString;
|
|
137
|
+
section: _sinclair_typebox.TString;
|
|
138
|
+
type: _sinclair_typebox.TString;
|
|
139
|
+
publishedVersion: _sinclair_typebox.TUnion<[_sinclair_typebox.TNumber, _sinclair_typebox.TNull]>;
|
|
140
|
+
createdBy: _sinclair_typebox.TUnion<[_sinclair_typebox.TString, _sinclair_typebox.TNull]>;
|
|
141
|
+
createdAt: _sinclair_typebox.TString;
|
|
142
|
+
updatedAt: _sinclair_typebox.TString;
|
|
143
|
+
}>, _sinclair_typebox.TObject<{
|
|
144
|
+
error: _sinclair_typebox.TString;
|
|
145
|
+
key: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
146
|
+
}>]>;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export { createLabelContract, deleteLabelContract, getLabelByKeyContract, getLabelContract, getLabelsContract, updateLabelContract };
|