@medipha/feed 0.1.0 → 0.1.1

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 (68) hide show
  1. package/dist/app/runtime/feed-runtime-context.d.ts +1 -1
  2. package/dist/app/runtime/feed-session.d.ts +1 -1
  3. package/dist/core/content.d.ts +19 -0
  4. package/dist/core/errors.d.ts +8 -0
  5. package/dist/core/feed-api.d.ts +134 -0
  6. package/dist/core/formatting.d.ts +55 -0
  7. package/dist/core/http.d.ts +18 -0
  8. package/dist/core/index.d.ts +16 -0
  9. package/dist/core/media-limits.d.ts +7 -0
  10. package/dist/core/mention.d.ts +10 -0
  11. package/dist/core/query-string.d.ts +3 -0
  12. package/dist/core/types.d.ts +208 -0
  13. package/dist/features/comments/comment-composer.d.ts +1 -1
  14. package/dist/features/comments/comment-item.d.ts +1 -1
  15. package/dist/features/comments/comment-thread.d.ts +1 -1
  16. package/dist/features/comments/mention-box.d.ts +1 -1
  17. package/dist/features/comments/mention-filter.d.ts +1 -1
  18. package/dist/features/comments/mention-input.d.ts +1 -1
  19. package/dist/features/comments/mention-options.d.ts +1 -1
  20. package/dist/features/comments/post-comments-modal.d.ts +1 -1
  21. package/dist/features/comments/use-mention-search.d.ts +1 -1
  22. package/dist/features/composer/category-select.d.ts +1 -1
  23. package/dist/features/composer/composer-form.d.ts +1 -1
  24. package/dist/features/composer/create-post-button.d.ts +1 -1
  25. package/dist/features/composer/create-post-modal.d.ts +1 -1
  26. package/dist/features/composer/format-text.d.ts +1 -1
  27. package/dist/features/composer/format-toolbar.d.ts +1 -1
  28. package/dist/features/composer/media-picker.d.ts +1 -1
  29. package/dist/features/composer/post-preview-modal.d.ts +1 -1
  30. package/dist/features/feed/left-rail.d.ts +1 -1
  31. package/dist/features/feed/sidebar/category-nav.d.ts +1 -1
  32. package/dist/features/feed/sidebar/discussion-channels.d.ts +1 -1
  33. package/dist/features/feed/widgets/post-detail-modal.d.ts +1 -1
  34. package/dist/features/feed/widgets/trending-hashtags-widget.d.ts +1 -1
  35. package/dist/features/manage/posts/post-summary-cell.d.ts +1 -1
  36. package/dist/features/manage/posts/review-drawer.d.ts +1 -1
  37. package/dist/features/manage/reports/overview-cards.d.ts +1 -1
  38. package/dist/features/manage/reports/top-posts-table.d.ts +1 -1
  39. package/dist/features/manage/reports/top-users-table.d.ts +1 -1
  40. package/dist/features/manage/reports/trend-chart.d.ts +1 -1
  41. package/dist/features/post/card/post-actions-menu.d.ts +1 -1
  42. package/dist/features/post/card/post-card.d.ts +1 -1
  43. package/dist/features/post/card/post-delete-button.d.ts +1 -1
  44. package/dist/features/post/card/post-header.d.ts +1 -1
  45. package/dist/features/post/card/post-status-tag.d.ts +1 -1
  46. package/dist/features/post/card/post-status.d.ts +1 -1
  47. package/dist/features/post/content/document-preview-url.d.ts +1 -1
  48. package/dist/features/post/content/mention-card.d.ts +1 -1
  49. package/dist/features/post/content/post-documents.d.ts +1 -1
  50. package/dist/features/post/content/post-images.d.ts +1 -1
  51. package/dist/features/post/content/post-videos.d.ts +1 -1
  52. package/dist/features/post/content/rich-content.d.ts +1 -1
  53. package/dist/features/post/content/use-link-preview.d.ts +1 -1
  54. package/dist/features/post/interactions/post-engagement-stats.d.ts +1 -1
  55. package/dist/features/post/interactions/post-reaction-bar.d.ts +1 -1
  56. package/dist/features/post/interactions/reaction-button.d.ts +1 -1
  57. package/dist/features/post/interactions/reaction-picker.d.ts +1 -1
  58. package/dist/features/post/interactions/share-button.d.ts +1 -1
  59. package/dist/features/post/list/post-list.d.ts +1 -1
  60. package/dist/index.cjs +21 -18
  61. package/dist/index.d.ts +1 -1
  62. package/dist/index.js +2761 -2325
  63. package/dist/shared/components/user-avatar.d.ts +1 -1
  64. package/dist/shared/hooks/use-cursor-list.d.ts +1 -1
  65. package/dist/shared/hooks/use-optimistic-reaction.d.ts +1 -1
  66. package/dist/shared/utils/date-range.d.ts +1 -1
  67. package/dist/shared/utils/reactions.d.ts +1 -1
  68. package/package.json +5 -9
@@ -1,4 +1,4 @@
1
- import { FeedApi, FeedMe } from '@medipha/feed-core';
1
+ import { FeedApi, FeedMe } from '../../core';
2
2
  import { NewsfeedModuleProps } from '../newsfeed-module.types';
3
3
  export interface FeedRuntime {
4
4
  api: FeedApi;
@@ -1,4 +1,4 @@
1
- import { FeedApi } from '@medipha/feed-core';
1
+ import { FeedApi } from '../../core';
2
2
  import { NewsfeedModuleProps } from '../newsfeed-module.types';
3
3
  type SessionProps = Pick<NewsfeedModuleProps, 'accessToken' | 'onRefreshToken' | 'onUnauthorized'>;
4
4
  export declare class FeedSession {
@@ -0,0 +1,19 @@
1
+ export type RichSegment = {
2
+ type: 'text';
3
+ text: string;
4
+ } | {
5
+ type: 'mention';
6
+ userId: string;
7
+ } | {
8
+ type: 'link';
9
+ url: string;
10
+ } | {
11
+ type: 'hashtag';
12
+ tag: string;
13
+ };
14
+ export declare function parseRichContent(content: string): RichSegment[];
15
+ export declare function extractHashtags(content: string): string[];
16
+ export declare function findFirstLink(content: string, ignorePrefix?: string): string | null;
17
+ export declare function getYoutubeVideoId(url: string): string | null;
18
+ export declare function youtubeThumbnailUrl(videoId: string): string;
19
+ export declare function excerpt(content: string, maxLength: number, mentionNames: Map<string, string>): string;
@@ -0,0 +1,8 @@
1
+ export declare const FEED_ERROR_CODES: readonly ["FEED_UNAUTHORIZED", "FEED_ERP_UNAVAILABLE", "FEED_POST_NOT_FOUND", "FEED_COMMENT_NOT_FOUND", "FEED_POST_FORBIDDEN", "FEED_FORBIDDEN", "FEED_POST_STATUS_INVALID", "FEED_POST_LOCKED", "FEED_POST_ALREADY_SHARED", "FEED_COMMENTS_LOCKED", "FEED_CATEGORY_NOT_FOUND", "FEED_CATEGORY_INVALID", "FEED_CATEGORY_IN_USE", "FEED_POST_EMPTY", "FEED_COMMENT_EMPTY", "FEED_MENTION_INVALID", "FEED_MEDIA_LIMIT_EXCEEDED", "FEED_MEDIA_INVALID", "FEED_MEDIA_KIND_INVALID", "FEED_MEDIA_UPLOAD_FAILED", "FEED_LINK_PREVIEW_URL_INVALID"];
2
+ export type FeedErrorCode = (typeof FEED_ERROR_CODES)[number];
3
+ export declare class FeedApiError extends Error {
4
+ readonly status: number;
5
+ readonly code?: string | undefined;
6
+ constructor(message: string, status: number, code?: string | undefined);
7
+ }
8
+ export declare function isFeedApiError(error: unknown, code?: FeedErrorCode): error is FeedApiError;
@@ -0,0 +1,134 @@
1
+ import { FeedApiConfig } from './http';
2
+ import { CursorPage, DepartmentEngagement, FeedBirthday, FeedCategory, FeedComment, FeedBranch, FeedDepartment, UserSearchFilter, FeedMe, FeedMedia, FeedMention, FeedPost, FeedUser, LinkPreview, ManageCounts, OffsetPage, PostStatus, ReactionSummary, ReactorPage, ReactionType, ReportGroupBy, ReportsOverview, ReportsTimeseriesPoint, ShareChannel, ShareResult, TopPostSort, TopUser, TrendingHashtag } from './types';
3
+ export interface CursorParams {
4
+ cursor?: string | null;
5
+ limit?: number;
6
+ }
7
+ export interface ListFeedPostsParams extends CursorParams {
8
+ q?: string;
9
+ categoryId?: string;
10
+ departmentId?: string;
11
+ hashtag?: string;
12
+ /** Inclusive day bounds (YYYY-MM-DD) on created_at. */
13
+ from?: string;
14
+ to?: string;
15
+ /** Reels: only posts with at least one image or video. */
16
+ hasMedia?: boolean;
17
+ /** Highlights: only the posts a manager pinned, newest pin first. */
18
+ pinned?: boolean;
19
+ }
20
+ export type MyPostsStatus = Exclude<PostStatus, 'DELETED'>;
21
+ export interface ListMyPostsParams extends CursorParams {
22
+ status?: MyPostsStatus;
23
+ }
24
+ export interface CreatePostInput {
25
+ clientPostId: string;
26
+ content: string;
27
+ mediaIds: string[];
28
+ categoryId?: string | null;
29
+ submit: boolean;
30
+ }
31
+ export interface UpdatePostInput {
32
+ content: string;
33
+ mediaIds: string[];
34
+ categoryId?: string | null;
35
+ }
36
+ export interface ListManagePostsParams {
37
+ q?: string;
38
+ status?: PostStatus;
39
+ authorId?: string;
40
+ departmentId?: string;
41
+ categoryId?: string;
42
+ from?: string;
43
+ to?: string;
44
+ page?: number;
45
+ pageSize?: number;
46
+ }
47
+ export interface CategoryInput {
48
+ name: string;
49
+ icon?: string | null;
50
+ color?: string | null;
51
+ sortOrder?: number;
52
+ hidden?: boolean;
53
+ }
54
+ export interface CreateCommentInput {
55
+ clientCommentId: string;
56
+ content: string;
57
+ /** Images uploaded first; the comment answers with them already attached. */
58
+ mediaIds?: string[];
59
+ parentId?: string | null;
60
+ }
61
+ /** Range of a report, optionally limited to one department (and its sub-departments). */
62
+ export type ReportFilterParams = DateRangeParams & {
63
+ departmentId?: string;
64
+ };
65
+ export interface DateRangeParams {
66
+ from: string;
67
+ to: string;
68
+ }
69
+ export declare function createFeedApi(config: FeedApiConfig): {
70
+ getMe: (signal?: AbortSignal) => Promise<FeedMe>;
71
+ searchUsers: (q: string, signal?: AbortSignal, filter?: UserSearchFilter) => Promise<FeedUser[]>;
72
+ listBranches: (signal?: AbortSignal) => Promise<FeedBranch[]>;
73
+ listDepartments: (q?: string, signal?: AbortSignal) => Promise<FeedDepartment[]>;
74
+ listFeedPosts: (params?: ListFeedPostsParams, signal?: AbortSignal) => Promise<CursorPage<FeedPost>>;
75
+ listCategories: (signal?: AbortSignal) => Promise<FeedCategory[]>;
76
+ listMentions: (params?: CursorParams, signal?: AbortSignal) => Promise<CursorPage<FeedMention>>;
77
+ listTodayBirthdays: (signal?: AbortSignal) => Promise<FeedBirthday[]>;
78
+ listTrendingHashtags: (signal?: AbortSignal) => Promise<TrendingHashtag[]>;
79
+ getPost: (postId: string, signal?: AbortSignal) => Promise<FeedPost>;
80
+ listMyPosts: (params?: ListMyPostsParams, signal?: AbortSignal) => Promise<CursorPage<FeedPost>>;
81
+ createPost: (input: CreatePostInput) => Promise<FeedPost>;
82
+ updatePost: (postId: string, input: UpdatePostInput) => Promise<FeedPost>;
83
+ submitPost: (postId: string) => Promise<FeedPost>;
84
+ deletePost: (postId: string) => Promise<void>;
85
+ uploadMedia: (file: Blob, fileName: string) => Promise<FeedMedia>;
86
+ listManagePosts: (params?: ListManagePostsParams, signal?: AbortSignal) => Promise<OffsetPage<FeedPost>>;
87
+ getManageCounts: (range?: Partial<DateRangeParams>, signal?: AbortSignal) => Promise<ManageCounts>;
88
+ approvePosts: (postIds: string[]) => Promise<void>;
89
+ pinPost: (postId: string) => Promise<FeedPost>;
90
+ unpinPost: (postId: string) => Promise<FeedPost>;
91
+ pinComment: (commentId: string) => Promise<FeedComment>;
92
+ unpinComment: (commentId: string) => Promise<FeedComment>;
93
+ /** Every category including hidden ones; managers only. */
94
+ listManageCategories: (signal?: AbortSignal) => Promise<FeedCategory[]>;
95
+ deleteCategory: (categoryId: string) => Promise<void>;
96
+ createCategory: (input: CategoryInput) => Promise<FeedCategory>;
97
+ updateCategory: (categoryId: string, input: Partial<CategoryInput>) => Promise<FeedCategory>;
98
+ approvePost: (postId: string) => Promise<FeedPost>;
99
+ /** APPROVED → PUBLISHED: the post goes live on the feed. */
100
+ publishPost: (postId: string) => Promise<FeedPost>;
101
+ /** PUBLISHED → UNPUBLISHED: takes the post off the feed so it can be edited or deleted. */
102
+ unpublishPost: (postId: string) => Promise<FeedPost>;
103
+ lockComments: (postId: string) => Promise<FeedPost>;
104
+ unlockComments: (postId: string) => Promise<FeedPost>;
105
+ setPostReaction: (postId: string, type: ReactionType) => Promise<ReactionSummary>;
106
+ listPostReactions: (postId: string, params?: CursorParams & {
107
+ type?: ReactionType;
108
+ }, signal?: AbortSignal) => Promise<ReactorPage>;
109
+ removePostReaction: (postId: string) => Promise<ReactionSummary>;
110
+ /** Counts one view of a live post; the server ignores repeats within 30 minutes. */
111
+ sharePost: (postId: string, channel: ShareChannel) => Promise<ShareResult>;
112
+ listComments: (postId: string, params?: CursorParams, signal?: AbortSignal) => Promise<CursorPage<FeedComment>>;
113
+ listReplies: (commentId: string, params?: CursorParams, signal?: AbortSignal) => Promise<CursorPage<FeedComment>>;
114
+ createComment: (postId: string, input: CreateCommentInput) => Promise<FeedComment>;
115
+ deleteComment: (commentId: string) => Promise<void>;
116
+ setCommentReaction: (commentId: string, type: ReactionType) => Promise<ReactionSummary>;
117
+ removeCommentReaction: (commentId: string) => Promise<ReactionSummary>;
118
+ getLinkPreview: (url: string, signal?: AbortSignal) => Promise<LinkPreview | undefined>;
119
+ getReportsOverview: (range: ReportFilterParams, signal?: AbortSignal) => Promise<ReportsOverview>;
120
+ getReportsTimeseries: (range: ReportFilterParams & {
121
+ groupBy: ReportGroupBy;
122
+ }, signal?: AbortSignal) => Promise<ReportsTimeseriesPoint[]>;
123
+ getTopPosts: (range: ReportFilterParams & {
124
+ sortBy?: TopPostSort;
125
+ limit?: number;
126
+ q?: string;
127
+ }, signal?: AbortSignal) => Promise<FeedPost[]>;
128
+ getEngagementByDepartment: (range: ReportFilterParams, signal?: AbortSignal) => Promise<DepartmentEngagement[]>;
129
+ getTopUsers: (range: ReportFilterParams & {
130
+ limit?: number;
131
+ userId?: string;
132
+ }, signal?: AbortSignal) => Promise<TopUser[]>;
133
+ };
134
+ export type FeedApi = ReturnType<typeof createFeedApi>;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Lightweight formatting stored inside the plain-text body, Markdown style, so clients that do
3
+ * not render it still show readable text and mentions, links and hashtags keep working.
4
+ *
5
+ * Inline: **bold**, *italic*, __underline__, ~~strike~~,
6
+ * {red|coloured}, {lg|bigger}, {blue lg|both} with a fixed palette and size scale.
7
+ * Lines: "# " heading, "- " bullet, "1. " numbered item, "> " quote.
8
+ */
9
+ export type InlineStyle = 'bold' | 'italic' | 'underline' | 'strike';
10
+ /** The only colours and sizes a post may use, so the feed stays consistent and nothing else gets in. */
11
+ export declare const TEXT_COLORS: readonly ["red", "orange", "green", "blue", "purple", "gray"];
12
+ export declare const TEXT_SIZES: readonly ["sm", "lg", "xl"];
13
+ export type TextColor = (typeof TEXT_COLORS)[number];
14
+ export type TextSize = (typeof TEXT_SIZES)[number];
15
+ export type InlineNode = {
16
+ type: 'text';
17
+ text: string;
18
+ } | {
19
+ type: InlineStyle;
20
+ text: string;
21
+ } | {
22
+ type: 'span';
23
+ text: string;
24
+ color?: TextColor;
25
+ size?: TextSize;
26
+ };
27
+ /** Two leading spaces make one indent level, up to MAX_INDENT; the renderer turns it into padding. */
28
+ export declare const MAX_INDENT = 4;
29
+ /** `indent` is only present when the block is indented, so unindented posts parse exactly as before. */
30
+ export type FormatBlock = ({
31
+ type: 'paragraph';
32
+ text: string;
33
+ } | {
34
+ type: 'heading';
35
+ text: string;
36
+ } | {
37
+ type: 'quote';
38
+ text: string;
39
+ } | {
40
+ type: 'bullets';
41
+ items: string[];
42
+ } | {
43
+ type: 'numbers';
44
+ items: string[];
45
+ }) & {
46
+ indent?: number;
47
+ };
48
+ /** Splits one line of text into plain and styled runs; styles do not nest. */
49
+ export declare function parseInline(text: string): InlineNode[];
50
+ /** Groups lines into blocks; consecutive list items form one list, other lines stay paragraphs. */
51
+ export declare function parseBlocks(content: string): FormatBlock[];
52
+ /** The text without any formatting markers, for excerpts, search snippets and notifications. */
53
+ export declare function stripFormatting(content: string): string;
54
+ /** True when the body uses any of the markers, so plain posts keep their exact old rendering. */
55
+ export declare function hasFormatting(content: string): boolean;
@@ -0,0 +1,18 @@
1
+ import { QueryParams } from './query-string';
2
+ export interface FeedApiConfig {
3
+ apiBaseUrl: string;
4
+ getAccessToken: () => string | null;
5
+ onRefreshToken?: () => Promise<string>;
6
+ onUnauthorized?: () => void;
7
+ fetchImpl?: typeof fetch;
8
+ }
9
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
10
+ export interface FeedRequestOptions {
11
+ method?: HttpMethod;
12
+ query?: QueryParams;
13
+ body?: unknown;
14
+ signal?: AbortSignal;
15
+ responseType?: 'json' | 'blob';
16
+ }
17
+ export type FeedRequest = <T>(path: string, options?: FeedRequestOptions) => Promise<T>;
18
+ export declare function createFeedRequest(config: FeedApiConfig): FeedRequest;
@@ -0,0 +1,16 @@
1
+ export { createFeedApi } from './feed-api';
2
+ export { MEDIA_MAX_MB, type MediaLimitKind } from './media-limits';
3
+ export { hasFormatting, MAX_INDENT, parseBlocks, parseInline, stripFormatting, TEXT_COLORS, TEXT_SIZES, type FormatBlock, type TextColor, type TextSize, type InlineNode, type InlineStyle, } from './formatting';
4
+ export type { CategoryInput, CreateCommentInput, CreatePostInput, CursorParams, DateRangeParams, ReportFilterParams, FeedApi, ListFeedPostsParams, ListManagePostsParams, ListMyPostsParams, MyPostsStatus, UpdatePostInput, } from './feed-api';
5
+ export { createFeedRequest } from './http';
6
+ export type { FeedApiConfig, FeedRequest, FeedRequestOptions, HttpMethod } from './http';
7
+ export { FEED_ERROR_CODES, FeedApiError, isFeedApiError } from './errors';
8
+ export type { FeedErrorCode } from './errors';
9
+ export { extractMentionIds, formatMention, parseContent } from './mention';
10
+ export type { ContentSegment } from './mention';
11
+ export { excerpt, extractHashtags, findFirstLink, getYoutubeVideoId, parseRichContent, youtubeThumbnailUrl, } from './content';
12
+ export type { RichSegment } from './content';
13
+ export { serializeQuery } from './query-string';
14
+ export type { QueryParams, QueryValue } from './query-string';
15
+ export { REACTION_TYPES } from './types';
16
+ export type { CursorPage, DepartmentEngagement, FeedBirthday, FeedCategory, FeedComment, FeedBranch, FeedDepartment, UserSearchFilter, FeedMe, FeedMedia, FeedMention, FeedPost, FeedUser, LinkPreview, LinkPreviewKind, ManageCounts, MediaKind, OffsetPage, PostStatus, ReactionSummary, FeedReactor, ReactorPage, ReactionType, ReportGroupBy, ReportsOverview, ReportsTimeseriesPoint, ShareChannel, ShareResult, TopPostSort, TopUser, TrendingHashtag, } from './types';
@@ -0,0 +1,7 @@
1
+ /** Biggest file a post or comment may carry, in MB, by kind. The backend enforces the same numbers. */
2
+ export declare const MEDIA_MAX_MB: {
3
+ readonly image: 10;
4
+ readonly document: 25;
5
+ readonly video: 100;
6
+ };
7
+ export type MediaLimitKind = keyof typeof MEDIA_MAX_MB;
@@ -0,0 +1,10 @@
1
+ export type ContentSegment = {
2
+ type: 'text';
3
+ text: string;
4
+ } | {
5
+ type: 'mention';
6
+ userId: string;
7
+ };
8
+ export declare function formatMention(userId: string): string;
9
+ export declare function parseContent(content: string): ContentSegment[];
10
+ export declare function extractMentionIds(content: string): string[];
@@ -0,0 +1,3 @@
1
+ export type QueryValue = string | number | boolean | null | undefined;
2
+ export type QueryParams = Record<string, QueryValue>;
3
+ export declare function serializeQuery(params?: QueryParams): string;
@@ -0,0 +1,208 @@
1
+ export type PostStatus = 'DRAFT' | 'PENDING' | 'APPROVED' | 'PUBLISHED' | 'UNPUBLISHED' | 'DELETED';
2
+ export type ReactionType = 'LIKE' | 'LOVE' | 'CLAP' | 'HAHA' | 'WOW' | 'SAD';
3
+ export declare const REACTION_TYPES: readonly ReactionType[];
4
+ export type MediaKind = 'IMAGE' | 'VIDEO' | 'DOCUMENT';
5
+ export type ShareChannel = 'CHAT' | 'ZALO' | 'FACEBOOK' | 'LINK';
6
+ export interface FeedUser {
7
+ id: string;
8
+ fullName: string;
9
+ avatarUrl: string | null;
10
+ departmentName: string | null;
11
+ jobTitle?: string | null;
12
+ isOfficial?: boolean;
13
+ }
14
+ export interface FeedMe {
15
+ userId: string;
16
+ user?: FeedUser;
17
+ canPost: boolean;
18
+ canManage: boolean;
19
+ requireApproval: boolean;
20
+ /** Biggest upload per kind in MB; absent on an older backend, the core defaults apply then. */
21
+ mediaLimitsMb?: {
22
+ image: number;
23
+ document: number;
24
+ video: number;
25
+ };
26
+ }
27
+ export interface FeedDepartment {
28
+ id: string;
29
+ name: string;
30
+ parentId: string | null;
31
+ level: number;
32
+ }
33
+ export interface FeedBranch {
34
+ id: string;
35
+ name: string;
36
+ }
37
+ /** Narrows the colleague search behind an "@" mention. */
38
+ export interface UserSearchFilter {
39
+ departmentId?: string;
40
+ branchId?: string;
41
+ }
42
+ export interface FeedCategory {
43
+ id: string;
44
+ name: string;
45
+ icon: string | null;
46
+ color: string | null;
47
+ sortOrder: number;
48
+ hidden: boolean;
49
+ postCount: number;
50
+ newCount: number;
51
+ }
52
+ export interface FeedMedia {
53
+ id: string;
54
+ kind: MediaKind;
55
+ fileName: string;
56
+ fileSize: number;
57
+ fileUrl: string;
58
+ mimeType: string;
59
+ width: number | null;
60
+ height: number | null;
61
+ thumbnailUrl?: string | null;
62
+ durationSeconds?: number | null;
63
+ }
64
+ export interface ReactionSummary {
65
+ reactionCount: number;
66
+ topReactionTypes: ReactionType[];
67
+ myReaction: ReactionType | null;
68
+ }
69
+ export interface FeedPost extends ReactionSummary {
70
+ id: string;
71
+ code?: string;
72
+ author: FeedUser;
73
+ content: string;
74
+ mentionedUsers: FeedUser[];
75
+ media: FeedMedia[];
76
+ category?: Pick<FeedCategory, 'id' | 'name' | 'color'> | null;
77
+ hashtags?: string[];
78
+ status: PostStatus;
79
+ pinnedAt?: string | null;
80
+ commentsLocked: boolean;
81
+ commentCount: number;
82
+ shareCount: number;
83
+ sharedByMe: boolean;
84
+ previewComments?: FeedComment[];
85
+ createdAt: string;
86
+ reviewedBy?: string | null;
87
+ reviewedAt?: string | null;
88
+ publishedBy?: string | null;
89
+ publishedAt?: string | null;
90
+ unpublishedBy?: string | null;
91
+ unpublishedAt?: string | null;
92
+ }
93
+ export interface FeedComment extends ReactionSummary {
94
+ id: string;
95
+ postId: string;
96
+ author: FeedUser;
97
+ content: string;
98
+ mentionedUsers: FeedUser[];
99
+ media: FeedMedia[];
100
+ parentId: string | null;
101
+ replyCount: number;
102
+ pinnedAt?: string | null;
103
+ createdAt: string;
104
+ editedAt: string | null;
105
+ }
106
+ export interface FeedReactor {
107
+ user: FeedUser;
108
+ type: ReactionType;
109
+ createdAt: string;
110
+ }
111
+ export interface ReactorPage {
112
+ items: FeedReactor[];
113
+ nextCursor: string | null;
114
+ tallies: {
115
+ type: ReactionType;
116
+ count: number;
117
+ }[];
118
+ }
119
+ export interface ShareResult {
120
+ shareCount: number;
121
+ sharedByMe: boolean;
122
+ }
123
+ export interface TrendingHashtag {
124
+ tag: string;
125
+ postCount: number;
126
+ interactionCount: number;
127
+ pinned?: boolean;
128
+ }
129
+ export interface FeedMention {
130
+ id: string;
131
+ postId: string;
132
+ commentId: string | null;
133
+ author: FeedUser;
134
+ excerpt: string;
135
+ createdAt: string;
136
+ }
137
+ export interface FeedBirthday {
138
+ user: FeedUser;
139
+ date: string;
140
+ }
141
+ export type LinkPreviewKind = 'YOUTUBE' | 'FACEBOOK' | 'GENERIC';
142
+ export interface LinkPreview {
143
+ url: string;
144
+ kind: LinkPreviewKind;
145
+ title: string | null;
146
+ description: string | null;
147
+ imageUrl: string | null;
148
+ siteName: string | null;
149
+ }
150
+ export interface CursorPage<T> {
151
+ items: T[];
152
+ nextCursor: string | null;
153
+ }
154
+ /** Status cards of the review queue. */
155
+ export interface ManageCounts {
156
+ PENDING: number;
157
+ /** Approved, waiting to be published. */
158
+ APPROVED: number;
159
+ PUBLISHED: number;
160
+ UNPUBLISHED: number;
161
+ DELETED: number;
162
+ }
163
+ export interface OffsetPage<T> {
164
+ items: T[];
165
+ total: number;
166
+ }
167
+ export type ReportGroupBy = 'day' | 'week' | 'month';
168
+ export type TopPostSort = 'total' | 'reactions' | 'comments';
169
+ export interface ReportsOverview {
170
+ postCount: number;
171
+ postCountChangePercent?: number | null;
172
+ postsByStatus?: Partial<Record<PostStatus, number>>;
173
+ reactionsByType: Partial<Record<ReactionType, number>>;
174
+ reactionTotal: number;
175
+ reactionChangePercent?: number | null;
176
+ commentCount: number;
177
+ commentChangePercent?: number | null;
178
+ rootCommentCount?: number;
179
+ replyCount?: number;
180
+ shareTotal: number;
181
+ shareChangePercent?: number | null;
182
+ sharesByChannel?: Partial<Record<ShareChannel, number>>;
183
+ }
184
+ export interface ReportsTimeseriesPoint {
185
+ bucket: string;
186
+ reactions: number;
187
+ comments: number;
188
+ }
189
+ export interface DepartmentEngagement {
190
+ departmentId: string;
191
+ departmentName: string;
192
+ interactionCount: number;
193
+ userCount?: number;
194
+ /** Share of all interactions in the range, 0..1. */
195
+ share: number;
196
+ }
197
+ export interface TopUser {
198
+ user: FeedUser;
199
+ /** The post this person interacted with the most in the range. */
200
+ topPost?: {
201
+ post: FeedPost;
202
+ interactions: number;
203
+ } | null;
204
+ reactions: number;
205
+ comments: number;
206
+ shares: number;
207
+ total: number;
208
+ }
@@ -1,4 +1,4 @@
1
- import { FeedComment, FeedUser } from '@medipha/feed-core';
1
+ import { FeedComment, FeedUser } from '../../core';
2
2
  interface CommentComposerProps {
3
3
  postId: string;
4
4
  parentId?: string;
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
- import { FeedComment } from '@medipha/feed-core';
2
+ import { FeedComment } from '../../core';
3
3
  interface CommentItemProps {
4
4
  comment: FeedComment;
5
5
  interactive: boolean;
@@ -1,4 +1,4 @@
1
- import { FeedComment, FeedPost } from '@medipha/feed-core';
1
+ import { FeedComment, FeedPost } from '../../core';
2
2
  interface CommentThreadProps {
3
3
  post: FeedPost;
4
4
  previewComments?: FeedComment[];
@@ -1,5 +1,5 @@
1
1
  import { MentionProps } from 'antd/lib/mentions';
2
- import { FeedUser } from '@medipha/feed-core';
2
+ import { FeedUser } from '../../core';
3
3
  /** "@" box shared by every composer: colleague search with a branch / department filter inside its menu. */
4
4
  export declare const MentionBox: import('react').ForwardRefExoticComponent<Omit<MentionProps, "children" | "onSelect" | "loading" | "onSearch"> & {
5
5
  onPick: (user: FeedUser) => void;
@@ -1,4 +1,4 @@
1
- import { UserSearchFilter } from '@medipha/feed-core';
1
+ import { UserSearchFilter } from '../../core';
2
2
  interface MentionFilterProps {
3
3
  value: UserSearchFilter;
4
4
  onChange: (filter: UserSearchFilter) => void;
@@ -1,4 +1,4 @@
1
- import { FeedUser } from '@medipha/feed-core';
1
+ import { FeedUser } from '../../core';
2
2
  export declare function toMentionTokens(text: string, picked: ReadonlyMap<string, FeedUser>): string;
3
3
  /** The reverse for editing: stored tokens back to "@Name" so the writer sees names, not ids. */
4
4
  export declare function fromMentionTokens(content: string, users: readonly FeedUser[]): string;
@@ -1,3 +1,3 @@
1
- import { FeedUser } from '@medipha/feed-core';
1
+ import { FeedUser } from '../../core';
2
2
  /** Mentions needs its Option elements as direct children, so this returns them instead of a component. */
3
3
  export declare function mentionOptions(users: FeedUser[]): import('react/jsx-runtime').JSX.Element[];
@@ -1,4 +1,4 @@
1
- import { FeedPost } from '@medipha/feed-core';
1
+ import { FeedPost } from '../../core';
2
2
  interface PostCommentsModalProps {
3
3
  post: FeedPost;
4
4
  open: boolean;
@@ -1,4 +1,4 @@
1
- import { FeedUser, UserSearchFilter } from '@medipha/feed-core';
1
+ import { FeedUser, UserSearchFilter } from '../../core';
2
2
  /** Debounced colleague search behind an "@" mention box. */
3
3
  export declare function useMentionSearch(): {
4
4
  suggestions: FeedUser[];
@@ -1,4 +1,4 @@
1
- import { FeedCategory } from '@medipha/feed-core';
1
+ import { FeedCategory } from '../../core';
2
2
  interface CategorySelectProps {
3
3
  categories: FeedCategory[];
4
4
  value: string | null;
@@ -1,4 +1,4 @@
1
- import { FeedCategory, FeedPost } from '@medipha/feed-core';
1
+ import { FeedCategory, FeedPost } from '../../core';
2
2
  export type ComposerIntent = 'text' | 'question';
3
3
  interface ComposerFormProps {
4
4
  categories?: FeedCategory[];
@@ -1,4 +1,4 @@
1
- import { FeedCategory, FeedPost } from '@medipha/feed-core';
1
+ import { FeedCategory, FeedPost } from '../../core';
2
2
  interface CreatePostButtonProps {
3
3
  categories: FeedCategory[];
4
4
  onSaved: (post: FeedPost) => void;
@@ -1,4 +1,4 @@
1
- import { FeedCategory, FeedPost } from '@medipha/feed-core';
1
+ import { FeedCategory, FeedPost } from '../../core';
2
2
  import { ComposerIntent } from './composer-form';
3
3
  interface CreatePostModalProps {
4
4
  open: boolean;
@@ -1,4 +1,4 @@
1
- import { TextColor, TextSize } from '@medipha/feed-core';
1
+ import { TextColor, TextSize } from '../../core';
2
2
  export type InlineFormat = 'bold' | 'italic' | 'underline' | 'strike';
3
3
  export type LineFormat = 'heading' | 'bullets' | 'numbers' | 'quote';
4
4
  export interface TextEdit {
@@ -1,4 +1,4 @@
1
- import { TextColor, TextSize } from '@medipha/feed-core';
1
+ import { TextColor, TextSize } from '../../core';
2
2
  import { InlineFormat, LineFormat } from './format-text';
3
3
  interface FormatToolbarProps {
4
4
  onInline: (format: InlineFormat) => void;
@@ -1,4 +1,4 @@
1
- import { FeedMedia } from '@medipha/feed-core';
1
+ import { FeedMedia } from '../../core';
2
2
  export declare const MAX_POST_MEDIA = 10;
3
3
  interface MediaPickerButtonsProps {
4
4
  count: number;
@@ -1,4 +1,4 @@
1
- import { FeedPost } from '@medipha/feed-core';
1
+ import { FeedPost } from '../../core';
2
2
  interface PostPreviewModalProps {
3
3
  post: FeedPost | null;
4
4
  onClose: () => void;
@@ -1,4 +1,4 @@
1
- import { FeedCategory } from '@medipha/feed-core';
1
+ import { FeedCategory } from '../../core';
2
2
  import { FeedFilters } from './use-feed-filters';
3
3
  interface LeftRailProps {
4
4
  categories: FeedCategory[];
@@ -1,4 +1,4 @@
1
- import { FeedCategory } from '@medipha/feed-core';
1
+ import { FeedCategory } from '../../../core';
2
2
  import { FeedFilters } from '../use-feed-filters';
3
3
  interface CategoryNavProps {
4
4
  categories: FeedCategory[];
@@ -1,4 +1,4 @@
1
- import { TrendingHashtag } from '@medipha/feed-core';
1
+ import { TrendingHashtag } from '../../../core';
2
2
  import { FeedFilters } from '../use-feed-filters';
3
3
  interface DiscussionChannelsProps {
4
4
  hashtags: TrendingHashtag[];
@@ -1,4 +1,4 @@
1
- import { FeedPost } from '@medipha/feed-core';
1
+ import { FeedPost } from '../../../core';
2
2
  interface PostDetailModalProps {
3
3
  post: FeedPost | null;
4
4
  onClose: () => void;