@ph-cms/client-sdk 0.1.8 → 0.1.9
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/README.md +132 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +2 -0
- package/dist/hooks/useContent.d.ts +15 -0
- package/dist/hooks/useContent.js +76 -1
- package/dist/hooks/useStampTour.d.ts +37 -0
- package/dist/hooks/useStampTour.js +39 -0
- package/dist/hooks/useUser.d.ts +26 -0
- package/dist/hooks/useUser.js +21 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/modules/content.d.ts +9 -1
- package/dist/modules/content.js +29 -0
- package/dist/modules/user.d.ts +17 -0
- package/dist/modules/user.js +22 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -516,6 +516,32 @@ function MyComponent() {
|
|
|
516
516
|
}
|
|
517
517
|
```
|
|
518
518
|
|
|
519
|
+
### Profile Update (`useUpdateProfile`)
|
|
520
|
+
|
|
521
|
+
사용자가 자신의 프로필을 수정할 수 있는 훅입니다. 업데이트가 성공하면 내부적으로 `refreshUser()`가 호출되어 컨텍스트와 UI가 즉각적으로 갱신됩니다.
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
import { useUpdateProfile } from '@ph-cms/client-sdk';
|
|
525
|
+
|
|
526
|
+
function ProfileEditor({ userUid }) {
|
|
527
|
+
const { mutateAsync: updateProfile, isPending } = useUpdateProfile();
|
|
528
|
+
|
|
529
|
+
const handleSave = async () => {
|
|
530
|
+
await updateProfile({
|
|
531
|
+
uid: userUid,
|
|
532
|
+
data: {
|
|
533
|
+
display_name: 'New Name',
|
|
534
|
+
avatar_url: 'https://example.com/new-avatar.png',
|
|
535
|
+
profile_data: { bio: 'Hello World' },
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
alert('프로필이 업데이트되었습니다.');
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
return <button onClick={handleSave} disabled={isPending}>저장</button>;
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
519
545
|
### Standalone (Non-React) Usage
|
|
520
546
|
|
|
521
547
|
React 없이 `PHCMSClient`를 직접 사용할 수 있습니다.
|
|
@@ -714,6 +740,105 @@ function ChannelList() {
|
|
|
714
740
|
|
|
715
741
|
---
|
|
716
742
|
|
|
743
|
+
## Like (좋아요)
|
|
744
|
+
|
|
745
|
+
콘텐츠에 좋아요를 남기고, 좋아요 상태를 확인할 수 있습니다.
|
|
746
|
+
|
|
747
|
+
### 좋아요 토글 (인증 필수)
|
|
748
|
+
|
|
749
|
+
```ts
|
|
750
|
+
// 모듈 직접 사용
|
|
751
|
+
const result = await client.content.toggleLike('content-uid');
|
|
752
|
+
// => { liked: true, likeCount: 42 }
|
|
753
|
+
|
|
754
|
+
// React Hook 사용 (Optimistic update 포함)
|
|
755
|
+
const { mutate: toggleLike } = useToggleLike();
|
|
756
|
+
toggleLike('content-uid');
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### 좋아요 상태 확인 (인증 선택)
|
|
760
|
+
|
|
761
|
+
```ts
|
|
762
|
+
// 모듈 직접 사용
|
|
763
|
+
const status = await client.content.getLikeStatus('content-uid');
|
|
764
|
+
// => { liked: true }
|
|
765
|
+
|
|
766
|
+
// React Hook 사용
|
|
767
|
+
const { data: status } = useLikeStatus('content-uid');
|
|
768
|
+
console.log(status?.liked); // true or false
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
## Stamp Tour
|
|
772
|
+
|
|
773
|
+
스탬프 투어 기능을 사용하여 특정 지점(Marker) 방문을 인증하고 진행 현황을 조회할 수 있습니다.
|
|
774
|
+
|
|
775
|
+
#### 스탬프 획득 (인증)
|
|
776
|
+
|
|
777
|
+
사용자의 현재 GPS 좌표를 전송하여 특정 투어 내의 마커 방문을 인증합니다.
|
|
778
|
+
|
|
779
|
+
```tsx
|
|
780
|
+
import { useStamp } from '@ph-cms/client-sdk';
|
|
781
|
+
|
|
782
|
+
function StampButton({ tourUid, markerUid }) {
|
|
783
|
+
const { mutateAsync: collectStamp, isPending } = useStamp();
|
|
784
|
+
|
|
785
|
+
const handleStamp = async () => {
|
|
786
|
+
try {
|
|
787
|
+
const result = await collectStamp({
|
|
788
|
+
tourUid,
|
|
789
|
+
markerUid,
|
|
790
|
+
data: { lat: 37.5511, lng: 126.9882 }
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
console.log(`거리: ${result.distance}m`);
|
|
794
|
+
if (result.isCompletion) {
|
|
795
|
+
alert('축하합니다! 투어를 완주하셨습니다.');
|
|
796
|
+
}
|
|
797
|
+
} catch (error) {
|
|
798
|
+
alert('스탬프를 획득할 수 없습니다. (너무 멀거나 이미 획득함)');
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
return <button onClick={handleStamp} disabled={isPending}>스탬프 찍기</button>;
|
|
803
|
+
}
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
#### 투어 진행 현황 조회
|
|
807
|
+
|
|
808
|
+
특정 투어에 대해 내가 획득한 스탬프 목록과 완주 여부를 확인합니다.
|
|
809
|
+
|
|
810
|
+
```tsx
|
|
811
|
+
import { useStampStatus } from '@ph-cms/client-sdk';
|
|
812
|
+
|
|
813
|
+
function TourProgress({ tourUid }) {
|
|
814
|
+
const { data: status, isLoading } = useStampStatus(tourUid);
|
|
815
|
+
|
|
816
|
+
if (isLoading) return <div>Loading...</div>;
|
|
817
|
+
|
|
818
|
+
return (
|
|
819
|
+
<div>
|
|
820
|
+
<p>진행도: {status?.collected_markers} / {status?.total_markers}</p>
|
|
821
|
+
{status?.is_completed && <p>🏆 완주 완료!</p>}
|
|
822
|
+
</div>
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
#### 투어 전체 통계 (관리자용)
|
|
828
|
+
|
|
829
|
+
투어의 총 참여자 수, 완주율 등 관리용 데이터를 조회합니다. (적절한 권한 필요)
|
|
830
|
+
|
|
831
|
+
```tsx
|
|
832
|
+
import { useTourStats } from '@ph-cms/client-sdk';
|
|
833
|
+
|
|
834
|
+
function AdminStats({ tourUid }) {
|
|
835
|
+
const { data: stats, isLoading } = useTourStats(tourUid);
|
|
836
|
+
// ...
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
---
|
|
841
|
+
|
|
717
842
|
## Media & File Upload
|
|
718
843
|
|
|
719
844
|
미디어 업로드는 3단계로 진행됩니다: Ticket 발급 → S3 업로드 → Content 생성/수정.
|
|
@@ -797,12 +922,19 @@ const client = new PHCMSClient({
|
|
|
797
922
|
client.authProvider // AuthProvider | undefined
|
|
798
923
|
client.axiosInstance // AxiosInstance — 내부 axios 인스턴스 직접 접근
|
|
799
924
|
client.auth // AuthModule
|
|
925
|
+
client.user // UserModule
|
|
800
926
|
client.content // ContentModule
|
|
801
927
|
client.channel // ChannelModule
|
|
802
928
|
client.terms // TermsModule
|
|
803
929
|
client.media // MediaModule
|
|
804
930
|
```
|
|
805
931
|
|
|
932
|
+
### `UserModule` (`client.user`)
|
|
933
|
+
|
|
934
|
+
| 메서드 | 설명 |
|
|
935
|
+
|---|---|
|
|
936
|
+
| `updateProfile(uid: string, data: UpdateUserProfileRequest)` | 유저의 프로필 정보 업데이트 (일반 유저는 `UpdateUserProfileRequest` 필드만 허용) → `UserDto` |
|
|
937
|
+
|
|
806
938
|
### `AuthModule` (`client.auth`)
|
|
807
939
|
|
|
808
940
|
| 메서드 | 설명 |
|
package/dist/client.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { ChannelModule } from './modules/channel';
|
|
|
5
5
|
import { ContentModule } from './modules/content';
|
|
6
6
|
import { MediaModule } from './modules/media';
|
|
7
7
|
import { TermsModule } from './modules/terms';
|
|
8
|
+
import { UserModule } from './modules/user';
|
|
8
9
|
export interface PHCMSClientConfig {
|
|
9
10
|
baseURL: string;
|
|
10
11
|
apiPrefix?: string;
|
|
@@ -19,6 +20,7 @@ export declare class PHCMSClient {
|
|
|
19
20
|
readonly channel: ChannelModule;
|
|
20
21
|
readonly terms: TermsModule;
|
|
21
22
|
readonly media: MediaModule;
|
|
23
|
+
readonly user: UserModule;
|
|
22
24
|
/**
|
|
23
25
|
* Whether a token refresh is currently in flight (used by the 401
|
|
24
26
|
* interceptor to de-duplicate concurrent refresh attempts).
|
package/dist/client.js
CHANGED
|
@@ -11,6 +11,7 @@ const channel_1 = require("./modules/channel");
|
|
|
11
11
|
const content_1 = require("./modules/content");
|
|
12
12
|
const media_1 = require("./modules/media");
|
|
13
13
|
const terms_1 = require("./modules/terms");
|
|
14
|
+
const user_1 = require("./modules/user");
|
|
14
15
|
class PHCMSClient {
|
|
15
16
|
/** Exposes the auth provider so UI layers can check token state synchronously. */
|
|
16
17
|
get authProvider() {
|
|
@@ -44,6 +45,7 @@ class PHCMSClient {
|
|
|
44
45
|
this.channel = new channel_1.ChannelModule(this.axiosInstance);
|
|
45
46
|
this.terms = new terms_1.TermsModule(this.axiosInstance);
|
|
46
47
|
this.media = new media_1.MediaModule(this.axiosInstance);
|
|
48
|
+
this.user = new user_1.UserModule(this.axiosInstance);
|
|
47
49
|
// Wire the refresh function into the auth provider so it can
|
|
48
50
|
// proactively refresh tokens inside `getToken()` without needing
|
|
49
51
|
// a direct reference to the AuthModule / axios instance.
|
|
@@ -27,6 +27,8 @@ export declare const contentKeys: {
|
|
|
27
27
|
}];
|
|
28
28
|
details: () => readonly ["contents", "detail"];
|
|
29
29
|
detail: (uid: string) => readonly ["contents", "detail", string];
|
|
30
|
+
likes: () => readonly ["contents", "like"];
|
|
31
|
+
like: (uid: string) => readonly ["contents", "like", string];
|
|
30
32
|
};
|
|
31
33
|
export declare const useContentList: (params: ListContentQuery, enabled?: boolean) => import("@tanstack/react-query").UseQueryResult<{
|
|
32
34
|
number: number;
|
|
@@ -64,6 +66,19 @@ export declare const useCreateContent: () => import("@tanstack/react-query").Use
|
|
|
64
66
|
sortOrder?: number | undefined;
|
|
65
67
|
mediaAttachments?: string[] | undefined;
|
|
66
68
|
}, unknown>;
|
|
69
|
+
export declare const useLikeStatus: (uid: string, enabled?: boolean) => import("@tanstack/react-query").UseQueryResult<{
|
|
70
|
+
liked: boolean;
|
|
71
|
+
}, Error>;
|
|
72
|
+
export declare const useToggleLike: () => import("@tanstack/react-query").UseMutationResult<{
|
|
73
|
+
liked: boolean;
|
|
74
|
+
likeCount: number;
|
|
75
|
+
}, Error, string, {
|
|
76
|
+
previousLikeStatus: {
|
|
77
|
+
liked: boolean;
|
|
78
|
+
} | undefined;
|
|
79
|
+
previousDetail: ContentDto | undefined;
|
|
80
|
+
uid: string;
|
|
81
|
+
}>;
|
|
67
82
|
export declare const useUpdateContent: () => import("@tanstack/react-query").UseMutationResult<ContentDto, Error, {
|
|
68
83
|
uid: string;
|
|
69
84
|
data: UpdateContentRequest;
|
package/dist/hooks/useContent.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useDeleteContent = exports.useUpdateContent = exports.useCreateContent = exports.useContentDetail = exports.useContentGeoList = exports.useContentList = exports.contentKeys = void 0;
|
|
3
|
+
exports.useDeleteContent = exports.useUpdateContent = exports.useToggleLike = exports.useLikeStatus = exports.useCreateContent = exports.useContentDetail = exports.useContentGeoList = exports.useContentList = exports.contentKeys = void 0;
|
|
4
4
|
const react_query_1 = require("@tanstack/react-query");
|
|
5
5
|
const context_1 = require("../context");
|
|
6
6
|
exports.contentKeys = {
|
|
@@ -11,6 +11,8 @@ exports.contentKeys = {
|
|
|
11
11
|
geoList: (bounds) => [...exports.contentKeys.geoLists(), bounds],
|
|
12
12
|
details: () => [...exports.contentKeys.all, 'detail'],
|
|
13
13
|
detail: (uid) => [...exports.contentKeys.details(), uid],
|
|
14
|
+
likes: () => [...exports.contentKeys.all, 'like'],
|
|
15
|
+
like: (uid) => [...exports.contentKeys.likes(), uid],
|
|
14
16
|
};
|
|
15
17
|
const useContentList = (params, enabled = true) => {
|
|
16
18
|
const client = (0, context_1.usePHCMS)();
|
|
@@ -51,6 +53,79 @@ const useCreateContent = () => {
|
|
|
51
53
|
});
|
|
52
54
|
};
|
|
53
55
|
exports.useCreateContent = useCreateContent;
|
|
56
|
+
const useLikeStatus = (uid, enabled = true) => {
|
|
57
|
+
const client = (0, context_1.usePHCMS)();
|
|
58
|
+
return (0, react_query_1.useQuery)({
|
|
59
|
+
queryKey: exports.contentKeys.like(uid),
|
|
60
|
+
queryFn: () => client.content.getLikeStatus(uid),
|
|
61
|
+
enabled: !!uid && enabled,
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
exports.useLikeStatus = useLikeStatus;
|
|
65
|
+
const useToggleLike = () => {
|
|
66
|
+
const client = (0, context_1.usePHCMS)();
|
|
67
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
68
|
+
return (0, react_query_1.useMutation)({
|
|
69
|
+
mutationFn: (uid) => client.content.toggleLike(uid),
|
|
70
|
+
onMutate: async (uid) => {
|
|
71
|
+
// Cancel any outgoing refetches so they don't overwrite our optimistic update
|
|
72
|
+
await queryClient.cancelQueries({ queryKey: exports.contentKeys.like(uid) });
|
|
73
|
+
await queryClient.cancelQueries({ queryKey: exports.contentKeys.detail(uid) });
|
|
74
|
+
// Snapshot the previous values
|
|
75
|
+
const previousLikeStatus = queryClient.getQueryData(exports.contentKeys.like(uid));
|
|
76
|
+
const previousDetail = queryClient.getQueryData(exports.contentKeys.detail(uid));
|
|
77
|
+
// Optimistically update to the new value
|
|
78
|
+
const isCurrentlyLiked = previousLikeStatus?.liked ?? previousDetail?.liked ?? false;
|
|
79
|
+
const newLikedState = !isCurrentlyLiked;
|
|
80
|
+
// Update like status
|
|
81
|
+
queryClient.setQueryData(exports.contentKeys.like(uid), {
|
|
82
|
+
liked: newLikedState,
|
|
83
|
+
});
|
|
84
|
+
// Update detail if it exists
|
|
85
|
+
if (previousDetail) {
|
|
86
|
+
queryClient.setQueryData(exports.contentKeys.detail(uid), {
|
|
87
|
+
...previousDetail,
|
|
88
|
+
liked: newLikedState,
|
|
89
|
+
stat: {
|
|
90
|
+
...previousDetail.stat,
|
|
91
|
+
like_count: previousDetail.stat.like_count + (newLikedState ? 1 : -1),
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// We could also optimistically update items in lists, but for simplicity we'll just invalidate on success/settled
|
|
96
|
+
// Return a context object with the snapshotted values
|
|
97
|
+
return { previousLikeStatus, previousDetail, uid };
|
|
98
|
+
},
|
|
99
|
+
onError: (err, uid, context) => {
|
|
100
|
+
// If the mutation fails, use the context returned from onMutate to roll back
|
|
101
|
+
if (context?.previousLikeStatus !== undefined) {
|
|
102
|
+
queryClient.setQueryData(exports.contentKeys.like(uid), context.previousLikeStatus);
|
|
103
|
+
}
|
|
104
|
+
if (context?.previousDetail !== undefined) {
|
|
105
|
+
queryClient.setQueryData(exports.contentKeys.detail(uid), context.previousDetail);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
onSuccess: (data, uid) => {
|
|
109
|
+
// In case the optimistic update got the count wrong or anything, set exact values from server
|
|
110
|
+
queryClient.setQueryData(exports.contentKeys.like(uid), { liked: data.liked });
|
|
111
|
+
queryClient.setQueryData(exports.contentKeys.detail(uid), (old) => {
|
|
112
|
+
if (!old)
|
|
113
|
+
return old;
|
|
114
|
+
return {
|
|
115
|
+
...old,
|
|
116
|
+
liked: data.liked,
|
|
117
|
+
stat: { ...old.stat, like_count: data.likeCount },
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
onSettled: (data, error, uid) => {
|
|
122
|
+
// Invalidate the lists so they fetch the latest liked status and like counts
|
|
123
|
+
queryClient.invalidateQueries({ queryKey: exports.contentKeys.lists() });
|
|
124
|
+
queryClient.invalidateQueries({ queryKey: exports.contentKeys.geoLists() });
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
exports.useToggleLike = useToggleLike;
|
|
54
129
|
const useUpdateContent = () => {
|
|
55
130
|
const client = (0, context_1.usePHCMS)();
|
|
56
131
|
const queryClient = (0, react_query_1.useQueryClient)();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CollectStampRequest } from '@ph-cms/api-contract';
|
|
2
|
+
export declare const stampTourKeys: {
|
|
3
|
+
all: readonly ["stamp-tours"];
|
|
4
|
+
status: (tourUid: string) => readonly ["stamp-tours", "status", string];
|
|
5
|
+
stats: (tourUid: string) => readonly ["stamp-tours", "stats", string];
|
|
6
|
+
};
|
|
7
|
+
export declare const useStamp: () => import("@tanstack/react-query").UseMutationResult<{
|
|
8
|
+
isCompletion: boolean;
|
|
9
|
+
distance: number;
|
|
10
|
+
}, Error, {
|
|
11
|
+
tourUid: string;
|
|
12
|
+
markerUid: string;
|
|
13
|
+
data: CollectStampRequest;
|
|
14
|
+
}, unknown>;
|
|
15
|
+
export declare const useStampStatus: (tourUid: string, enabled?: boolean) => import("@tanstack/react-query").UseQueryResult<{
|
|
16
|
+
total_markers: number;
|
|
17
|
+
collected_markers: number;
|
|
18
|
+
is_completed: boolean;
|
|
19
|
+
stamps: {
|
|
20
|
+
marker_uid: string;
|
|
21
|
+
collected_at: string;
|
|
22
|
+
}[];
|
|
23
|
+
}, Error>;
|
|
24
|
+
export declare const useTourStats: (tourUid: string, enabled?: boolean) => import("@tanstack/react-query").UseQueryResult<{
|
|
25
|
+
total_markers: number;
|
|
26
|
+
tour_uid: string;
|
|
27
|
+
tour_title: string;
|
|
28
|
+
total_participants: number;
|
|
29
|
+
total_completions: number;
|
|
30
|
+
completion_rate: number;
|
|
31
|
+
avg_distance_meter: number;
|
|
32
|
+
stamps_by_marker: {
|
|
33
|
+
marker_uid: string;
|
|
34
|
+
marker_title: string;
|
|
35
|
+
stamp_count: number;
|
|
36
|
+
}[];
|
|
37
|
+
}, Error>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useTourStats = exports.useStampStatus = exports.useStamp = exports.stampTourKeys = void 0;
|
|
4
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
5
|
+
const context_1 = require("../context");
|
|
6
|
+
exports.stampTourKeys = {
|
|
7
|
+
all: ['stamp-tours'],
|
|
8
|
+
status: (tourUid) => [...exports.stampTourKeys.all, 'status', tourUid],
|
|
9
|
+
stats: (tourUid) => [...exports.stampTourKeys.all, 'stats', tourUid],
|
|
10
|
+
};
|
|
11
|
+
const useStamp = () => {
|
|
12
|
+
const client = (0, context_1.usePHCMS)();
|
|
13
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
14
|
+
return (0, react_query_1.useMutation)({
|
|
15
|
+
mutationFn: ({ tourUid, markerUid, data }) => client.content.stamp(tourUid, markerUid, data),
|
|
16
|
+
onSuccess: (data, variables) => {
|
|
17
|
+
queryClient.invalidateQueries({ queryKey: exports.stampTourKeys.status(variables.tourUid) });
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
exports.useStamp = useStamp;
|
|
22
|
+
const useStampStatus = (tourUid, enabled = true) => {
|
|
23
|
+
const client = (0, context_1.usePHCMS)();
|
|
24
|
+
return (0, react_query_1.useQuery)({
|
|
25
|
+
queryKey: exports.stampTourKeys.status(tourUid),
|
|
26
|
+
queryFn: () => client.content.getStampStatus(tourUid),
|
|
27
|
+
enabled: !!tourUid && enabled,
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
exports.useStampStatus = useStampStatus;
|
|
31
|
+
const useTourStats = (tourUid, enabled = true) => {
|
|
32
|
+
const client = (0, context_1.usePHCMS)();
|
|
33
|
+
return (0, react_query_1.useQuery)({
|
|
34
|
+
queryKey: exports.stampTourKeys.stats(tourUid),
|
|
35
|
+
queryFn: () => client.content.getTourStats(tourUid),
|
|
36
|
+
enabled: !!tourUid && enabled,
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
exports.useTourStats = useTourStats;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { UpdateUserProfileRequest } from '@ph-cms/api-contract';
|
|
2
|
+
/**
|
|
3
|
+
* Hook to update a user's profile.
|
|
4
|
+
* Typically used by a user to update their own profile.
|
|
5
|
+
*/
|
|
6
|
+
export declare const useUpdateProfile: () => import("@tanstack/react-query").UseMutationResult<{
|
|
7
|
+
uid: string;
|
|
8
|
+
email: string;
|
|
9
|
+
username: string | null;
|
|
10
|
+
display_name: string;
|
|
11
|
+
avatar_url: string | null;
|
|
12
|
+
phone_number: string | null;
|
|
13
|
+
email_verified_at: string | null;
|
|
14
|
+
phone_verified_at: string | null;
|
|
15
|
+
locale: string;
|
|
16
|
+
timezone: string;
|
|
17
|
+
status: string;
|
|
18
|
+
role: string[];
|
|
19
|
+
profile_data: Record<string, any>;
|
|
20
|
+
last_login_at: string | null;
|
|
21
|
+
created_at: string;
|
|
22
|
+
updated_at: string;
|
|
23
|
+
}, Error, {
|
|
24
|
+
uid: string;
|
|
25
|
+
data: UpdateUserProfileRequest | Record<string, any>;
|
|
26
|
+
}, unknown>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useUpdateProfile = void 0;
|
|
4
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
5
|
+
const context_1 = require("../context");
|
|
6
|
+
/**
|
|
7
|
+
* Hook to update a user's profile.
|
|
8
|
+
* Typically used by a user to update their own profile.
|
|
9
|
+
*/
|
|
10
|
+
const useUpdateProfile = () => {
|
|
11
|
+
const client = (0, context_1.usePHCMS)();
|
|
12
|
+
const { refreshUser } = (0, context_1.usePHCMSContext)();
|
|
13
|
+
return (0, react_query_1.useMutation)({
|
|
14
|
+
mutationFn: (params) => client.user.updateProfile(params.uid, params.data),
|
|
15
|
+
onSuccess: async () => {
|
|
16
|
+
// Refresh user in context after a successful update so the UI gets the new data
|
|
17
|
+
await refreshUser();
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
exports.useUpdateProfile = useUpdateProfile;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,11 +10,14 @@ export * from './modules/channel';
|
|
|
10
10
|
export * from './modules/content';
|
|
11
11
|
export * from './modules/media';
|
|
12
12
|
export * from './modules/terms';
|
|
13
|
+
export * from './modules/user';
|
|
13
14
|
export * from './context';
|
|
14
15
|
export * from './hooks/useAuth';
|
|
15
16
|
export * from './hooks/useChannel';
|
|
16
17
|
export * from './hooks/useContent';
|
|
18
|
+
export * from './hooks/useStampTour';
|
|
17
19
|
export * from './hooks/useFirebaseAuthSync';
|
|
18
20
|
export * from './hooks/useMedia';
|
|
19
21
|
export * from './hooks/useTerms';
|
|
22
|
+
export * from './hooks/useUser';
|
|
20
23
|
export * from './types';
|
package/dist/index.js
CHANGED
|
@@ -26,11 +26,14 @@ __exportStar(require("./modules/channel"), exports);
|
|
|
26
26
|
__exportStar(require("./modules/content"), exports);
|
|
27
27
|
__exportStar(require("./modules/media"), exports);
|
|
28
28
|
__exportStar(require("./modules/terms"), exports);
|
|
29
|
+
__exportStar(require("./modules/user"), exports);
|
|
29
30
|
__exportStar(require("./context"), exports);
|
|
30
31
|
__exportStar(require("./hooks/useAuth"), exports);
|
|
31
32
|
__exportStar(require("./hooks/useChannel"), exports);
|
|
32
33
|
__exportStar(require("./hooks/useContent"), exports);
|
|
34
|
+
__exportStar(require("./hooks/useStampTour"), exports);
|
|
33
35
|
__exportStar(require("./hooks/useFirebaseAuthSync"), exports);
|
|
34
36
|
__exportStar(require("./hooks/useMedia"), exports);
|
|
35
37
|
__exportStar(require("./hooks/useTerms"), exports);
|
|
38
|
+
__exportStar(require("./hooks/useUser"), exports);
|
|
36
39
|
__exportStar(require("./types"), exports);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AxiosInstance } from "axios";
|
|
2
|
-
import { CreateContentRequest, UpdateContentRequest, ListContentQuery, ContentDto, PagedContentListResponse, BoundsQuery } from "@ph-cms/api-contract";
|
|
2
|
+
import { CreateContentRequest, UpdateContentRequest, ListContentQuery, ContentDto, PagedContentListResponse, BoundsQuery, CollectStampRequest, StampStatusDto, TourStatsDto, ToggleLikeResponse, LikeStatusResponse } from "@ph-cms/api-contract";
|
|
3
3
|
export declare class ContentModule {
|
|
4
4
|
private client;
|
|
5
5
|
constructor(client: AxiosInstance);
|
|
@@ -9,4 +9,12 @@ export declare class ContentModule {
|
|
|
9
9
|
create(data: CreateContentRequest): Promise<ContentDto>;
|
|
10
10
|
update(uid: string, data: UpdateContentRequest): Promise<ContentDto>;
|
|
11
11
|
delete(uid: string): Promise<void>;
|
|
12
|
+
stamp(tourUid: string, markerUid: string, data: CollectStampRequest): Promise<{
|
|
13
|
+
isCompletion: boolean;
|
|
14
|
+
distance: number;
|
|
15
|
+
}>;
|
|
16
|
+
getStampStatus(tourUid: string): Promise<StampStatusDto>;
|
|
17
|
+
getTourStats(tourUid: string): Promise<TourStatsDto>;
|
|
18
|
+
toggleLike(uid: string): Promise<ToggleLikeResponse>;
|
|
19
|
+
getLikeStatus(uid: string): Promise<LikeStatusResponse>;
|
|
12
20
|
}
|
package/dist/modules/content.js
CHANGED
|
@@ -47,5 +47,34 @@ class ContentModule {
|
|
|
47
47
|
throw new errors_1.ValidationError("UID is required", []);
|
|
48
48
|
return this.client.delete(`/api/contents/${uid}`);
|
|
49
49
|
}
|
|
50
|
+
async stamp(tourUid, markerUid, data) {
|
|
51
|
+
if (!tourUid || !markerUid)
|
|
52
|
+
throw new errors_1.ValidationError("Both tourUid and markerUid are required", []);
|
|
53
|
+
const validation = api_contract_1.CollectStampSchema.safeParse(data);
|
|
54
|
+
if (!validation.success) {
|
|
55
|
+
throw new errors_1.ValidationError("Invalid stamp data", validation.error.errors);
|
|
56
|
+
}
|
|
57
|
+
return this.client.post(`/api/contents/${tourUid}/stamp/${markerUid}`, data);
|
|
58
|
+
}
|
|
59
|
+
async getStampStatus(tourUid) {
|
|
60
|
+
if (!tourUid)
|
|
61
|
+
throw new errors_1.ValidationError("tourUid is required", []);
|
|
62
|
+
return this.client.get(`/api/contents/${tourUid}/stamp-status`);
|
|
63
|
+
}
|
|
64
|
+
async getTourStats(tourUid) {
|
|
65
|
+
if (!tourUid)
|
|
66
|
+
throw new errors_1.ValidationError("tourUid is required", []);
|
|
67
|
+
return this.client.get(`/api/contents/${tourUid}/stamp-stats`);
|
|
68
|
+
}
|
|
69
|
+
async toggleLike(uid) {
|
|
70
|
+
if (!uid)
|
|
71
|
+
throw new errors_1.ValidationError("UID is required", []);
|
|
72
|
+
return this.client.post(`/api/contents/${uid}/like`);
|
|
73
|
+
}
|
|
74
|
+
async getLikeStatus(uid) {
|
|
75
|
+
if (!uid)
|
|
76
|
+
throw new errors_1.ValidationError("UID is required", []);
|
|
77
|
+
return this.client.get(`/api/contents/${uid}/like`);
|
|
78
|
+
}
|
|
50
79
|
}
|
|
51
80
|
exports.ContentModule = ContentModule;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { UpdateUserProfileRequest, UserDto } from "@ph-cms/api-contract";
|
|
2
|
+
import { AxiosInstance } from "axios";
|
|
3
|
+
export declare class UserModule {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(client: AxiosInstance);
|
|
6
|
+
/**
|
|
7
|
+
* Updates a user's profile.
|
|
8
|
+
*
|
|
9
|
+
* Normal users can only update specific fields defined in `UpdateUserProfileRequest`
|
|
10
|
+
* (e.g., `display_name`, `avatar_url`, `profile_data`).
|
|
11
|
+
* Admin users can pass any updatable fields.
|
|
12
|
+
*
|
|
13
|
+
* @param uid - The UID of the user to update.
|
|
14
|
+
* @param data - The update payload.
|
|
15
|
+
*/
|
|
16
|
+
updateProfile(uid: string, data: UpdateUserProfileRequest | Record<string, any>): Promise<UserDto>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserModule = void 0;
|
|
4
|
+
class UserModule {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Updates a user's profile.
|
|
10
|
+
*
|
|
11
|
+
* Normal users can only update specific fields defined in `UpdateUserProfileRequest`
|
|
12
|
+
* (e.g., `display_name`, `avatar_url`, `profile_data`).
|
|
13
|
+
* Admin users can pass any updatable fields.
|
|
14
|
+
*
|
|
15
|
+
* @param uid - The UID of the user to update.
|
|
16
|
+
* @param data - The update payload.
|
|
17
|
+
*/
|
|
18
|
+
async updateProfile(uid, data) {
|
|
19
|
+
return this.client.patch(`/api/users/${uid}`, data);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.UserModule = UserModule;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ph-cms/client-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Unified PH-CMS Client SDK (React + Core)",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"LICENSE"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ph-cms/api-contract": "
|
|
24
|
+
"@ph-cms/api-contract": "0.1.2",
|
|
25
25
|
"@tanstack/react-query": "^5.0.0",
|
|
26
26
|
"axios": "^1.6.0",
|
|
27
27
|
"zod": "^3.22.4"
|