Skip to content

Commit

Permalink
feat(ui/dashboard): user segments management (#1426)
Browse files Browse the repository at this point in the history
  • Loading branch information
steveninhle authored Jan 15, 2025
1 parent cfe76a0 commit bd8710e
Show file tree
Hide file tree
Showing 43 changed files with 2,101 additions and 20 deletions.
12 changes: 11 additions & 1 deletion ui/dashboard/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,15 @@
"edit-push": "Update Push Notification",
"new-push-subtitle": "Every time a feature flag is updated, a push notification is sent to the SDK client allowing you to update the SDK cache in real-time.",
"tags": "Tags",
"fcm-api-key": "Firebase Service Account"
"fcm-api-key": "Firebase Service Account",
"user-segments": "User Segments",
"user-segments-subtitle": "You can create segments to help you start testing feature flags for specifics type of users",
"new-user-segment": "New User Segment",
"update-user-segment": "Update User Segment",
"delete-user-segment": "Delete User Segment",
"connections": "Connections",
"status": "Status",
"flags-connected": "Flags Connected",
"flags-connected-desc": "This user segment is currently connected to the following feature flags:",
"close": "Close"
}
9 changes: 8 additions & 1 deletion ui/dashboard/public/locales/en/form.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
"account": "Get notified when someone adds or updates an account",
"notification": "Get notified when someone adds or updates a notification"
},
"upload-files": "Drop your file here, or <underline>{{text}}</underline>",
"placeholder-search-user-segments": "Search user segment",
"list-of-users-ids": "List Of Users IDs",
"browse-files": "Browse files",
"enter-user-ids": "Enter user IDs",
"upload-files": "Drop your files here, or <underline>{{text}}</underline>",
"update-user-segment": "Updating this field will replace the current list with the new one.",
"update-user-segment-warning": "<p>The user ID list can't be updated because {{count}} flag is using it. Remove the segment from the flag before updating it.</p>",
"placeholder-enter-user-ids": "Enter IDs separated by commas (E.g., userId1, userId2, userId3,...)",
"accept-file-types": "{{type}}"
}
13 changes: 11 additions & 2 deletions ui/dashboard/public/locales/en/table.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"flags": "Flags",
"created-at": "Created At",
"updated-at": "Updated At",
"last-seen": "Last Seen",
"popover": {
"edit-project": "Edit Project",
Expand All @@ -23,7 +24,10 @@
"enable-notification": "Enable Notification",
"edit-push": "Edit Push",
"disable-push": "Disable Push",
"enable-push": "Enable Push"
"enable-push": "Enable Push",
"edit-segment": "Edit User Segment",
"delete-segment": "Delete User Segment",
"download-segment": "Download User Segment"
},
"empty": {
"project-title": "No registered projects",
Expand All @@ -40,7 +44,9 @@
"notification-title": "No registered notifications",
"notification-desc": "There are no registered notifications. Add a new one to start managing.",
"push-title": "No registered pushes",
"push-desc": "There are no registered pushes. Add a new one to start managing."
"push-desc": "There are no registered pushes. Add a new one to start managing.",
"user-segments-title": "No registered user segments",
"user-segments-desc": "There are no registered user segments. Add a new one to start managing."
},
"organization": {
"confirm-archive-desc": "The organization \"<bold>{{name}}</bold>\" will be archived. Are you sure you want to proceed?",
Expand All @@ -66,5 +72,8 @@
"push": {
"confirm-enable-desc": "The Push \"<bold>{{name}}</bold>\" will be enabled. Are you sure you want to proceed?",
"confirm-disable-desc": "The Push \"<bold>{{name}}</bold>\" will be disabled. Are you sure you want to proceed?"
},
"user-segment": {
"delete-user-segment-desc": "The <bold>{{name}}</bold> is going to be deleted permanently. Are you sure you want to proceed?"
}
}
12 changes: 11 additions & 1 deletion ui/dashboard/public/locales/ja/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,15 @@
"edit-push": "Update Push Notification",
"new-push-subtitle": "Every time a feature flag is updated, a push notification is sent to the SDK client allowing you to update the SDK cache in real-time.",
"tags": "Tags",
"fcm-api-key": "Firebase Service Account"
"fcm-api-key": "Firebase Service Account",
"user-segments": "User Segments",
"user-segments-subtitle": "You can create segments to help you start testing feature flags for specifics type of users",
"new-user-segment": "New User Segment",
"update-user-segment": "Update User Segment",
"delete-user-segment": "Delete User Segment",
"connections": "Connections",
"status": "Status",
"flags-connected": "Flags Connected",
"flags-connected-desc": "This user segment is currently connected to the following feature flags:",
"close": "Close"
}
9 changes: 8 additions & 1 deletion ui/dashboard/public/locales/ja/form.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
"account": "Get notified when someone adds or updates an account",
"notification": "Get notified when someone adds or updates a notification"
},
"upload-files": "Drop your file here, or <underline>{{text}}</underline>",
"placeholder-search-user-segments": "Search user segment",
"list-of-users-ids": "List Of Users IDs",
"browse-files": "Browse files",
"enter-user-ids": "Enter user IDs",
"upload-files": "Drop your files here, or <underline>{{text}}</underline>",
"update-user-segment": "Updating this field will replace the current list with the new one.",
"update-user-segment-warning": "<p>The user ID list can't be updated because {{count}} flag is using it. Remove the segment from the flag before updating it.</p>",
"placeholder-enter-user-ids": "Enter IDs separated by commas (E.g., userId1, userId2, userId3,...)",
"accept-file-types": "{{type}}"
}
13 changes: 11 additions & 2 deletions ui/dashboard/public/locales/ja/table.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"flags": "Flags",
"created-at": "Created At",
"updated-at": "Updated At",
"last-seen": "Last Seen",
"popover": {
"edit-project": "Edit Project",
Expand All @@ -23,7 +24,10 @@
"enable-notification": "Enable Notification",
"edit-push": "Edit Push",
"disable-push": "Disable Push",
"enable-push": "Enable Push"
"enable-push": "Enable Push",
"edit-segment": "Edit User Segment",
"delete-segment": "Delete User Segment",
"download-segment": "Download User Segment"
},
"empty": {
"project-title": "No registered projects",
Expand All @@ -40,7 +44,9 @@
"notification-title": "No registered notifications",
"notification-desc": "There are no registered notifications. Add a new one to start managing.",
"push-title": "No registered pushes",
"push-desc": "There are no registered pushes. Add a new one to start managing."
"push-desc": "There are no registered pushes. Add a new one to start managing.",
"user-segments-title": "No registered user segments",
"user-segments-desc": "There are no registered user segments. Add a new one to start managing."
},
"organization": {
"confirm-archive-desc": "The organization \"<bold>{{name}}</bold>\" will be archived. Are you sure you want to proceed?",
Expand All @@ -66,5 +72,8 @@
"push": {
"confirm-enable-desc": "The Push \"<bold>{{name}}</bold>\" will be enabled. Are you sure you want to proceed?",
"confirm-disable-desc": "The Push \"<bold>{{name}}</bold>\" will be disabled. Are you sure you want to proceed?"
},
"user-segment": {
"delete-user-segment-desc": "The <bold>{{name}}</bold> is going to be deleted permanently. Are you sure you want to proceed?"
}
}
5 changes: 5 additions & 0 deletions ui/dashboard/src/@api/user-segment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './user-segments-fetcher';
export * from './user-segment-creator';
export * from './user-segment-bulk-upload';
export * from './user-segment-fetcher';
export * from './user-segment-bulk-download';
23 changes: 23 additions & 0 deletions ui/dashboard/src/@api/user-segment/user-segment-bulk-download.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import axiosClient from '@api/axios-client';
import pickBy from 'lodash/pickBy';
import { isNotEmpty } from 'utils/data-type';

export interface UserSegmentBulkDownloadParams {
segmentId: string;
environmentId: string;
state?: 'INCLUDED' | 'EXCLUDED';
}

export interface UserSegmentBulkDownloadResponse {
data: string;
}

export const userSegmentBulkDownload = async (
params?: UserSegmentBulkDownloadParams
): Promise<UserSegmentBulkDownloadResponse> => {
return axiosClient
.get<UserSegmentBulkDownloadResponse>('/v1/segment_users/bulk_download', {
params: pickBy(params, v => isNotEmpty(v))
})
.then(response => response.data);
};
16 changes: 16 additions & 0 deletions ui/dashboard/src/@api/user-segment/user-segment-bulk-upload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import axiosClient from '@api/axios-client';

export interface UserSegmentBulkUploadParams {
segmentId: string;
environmentId?: string;
data?: string;
state?: 'INCLUDED' | 'EXCLUDED';
}

export const userSegmentBulkUpload = async (
params?: UserSegmentBulkUploadParams
) => {
return axiosClient
.post('/v1/segment_users/bulk_upload', params)
.then(response => response.data);
};
20 changes: 20 additions & 0 deletions ui/dashboard/src/@api/user-segment/user-segment-creator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import axiosClient from '@api/axios-client';
import { UserSegment } from '@types';

export interface UserSegmentCreatorParams {
environmentId: string;
name: string;
description?: string;
}

export interface UserSegmentCreatorResponse {
segment: UserSegment;
}

export const userSegmentCreator = async (
params?: UserSegmentCreatorParams
): Promise<UserSegmentCreatorResponse> => {
return axiosClient
.post<UserSegmentCreatorResponse>('/v1/segment', params)
.then(response => response.data);
};
17 changes: 17 additions & 0 deletions ui/dashboard/src/@api/user-segment/user-segment-delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import axiosClient from '@api/axios-client';
import { pickBy } from 'lodash';
import { isNotEmpty } from 'utils/data-type';
import { stringifyParams } from 'utils/search-params';

export interface UserSegmentDeleteParams {
id: string;
environmentId: string;
}

export const userSegmentDelete = async (_params?: UserSegmentDeleteParams) => {
const params = pickBy(_params, v => isNotEmpty(v));

return axiosClient
.delete(`/v1/segment?${stringifyParams(params)}`)
.then(response => response.data);
};
23 changes: 23 additions & 0 deletions ui/dashboard/src/@api/user-segment/user-segment-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import axiosClient from '@api/axios-client';
import pickBy from 'lodash/pickBy';
import { UserSegment } from '@types';
import { isNotEmpty } from 'utils/data-type';

export interface UserSegmentFetcherParams {
id: string;
environmentId: string;
}

export interface UserSegmentResponse {
segment: Array<UserSegment>;
}

export const userSegmentFetcher = async (
params?: UserSegmentFetcherParams
): Promise<UserSegmentResponse> => {
return axiosClient
.get<UserSegmentResponse>('/v1/segment', {
params: pickBy(params, v => isNotEmpty(v))
})
.then(response => response.data);
};
21 changes: 21 additions & 0 deletions ui/dashboard/src/@api/user-segment/user-segment-updater.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import axiosClient from '@api/axios-client';
import { UserSegment } from '@types';

export interface UserSegmentUpdaterParams {
id: string;
environmentId: string;
name?: string;
description?: string;
}

export interface UserSegmentUpdaterResponse {
segment: UserSegment;
}

export const userSegmentUpdater = async (
params?: UserSegmentUpdaterParams
): Promise<UserSegmentUpdaterResponse> => {
return axiosClient
.patch<UserSegmentUpdaterResponse>('/v1/segment', params)
.then(response => response.data);
};
25 changes: 25 additions & 0 deletions ui/dashboard/src/@api/user-segment/user-segments-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import axiosClient from '@api/axios-client';
import pickBy from 'lodash/pickBy';
import {
UserSegmentCollection,
CollectionParams,
FeatureSegmentStatus
} from '@types';
import { isNotEmpty } from 'utils/data-type';
import { stringifyParams } from 'utils/search-params';

export interface UserSegmentsFetcherParams extends CollectionParams {
environmentId?: string;
isInUseStatus?: boolean;
status?: FeatureSegmentStatus;
}

export const userSegmentsFetcher = async (
params?: UserSegmentsFetcherParams
): Promise<UserSegmentCollection> => {
const requestParams = stringifyParams(pickBy(params, v => isNotEmpty(v)));

return axiosClient
.get<UserSegmentCollection>(`/v1/segments?${requestParams}`)
.then(response => response.data);
};
4 changes: 3 additions & 1 deletion ui/dashboard/src/@icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import IconUsage from './sidebar-icons/usage.svg?react';
import IconUser from './sidebar-icons/user.svg?react';
import IconDelete from './special-icons/delete.svg?react';
import IconFCM from './special-icons/fcm.svg?react';
import IconFlagConnected from './special-icons/flag-connected.svg?react';
import IconGithub from './special-icons/github.svg?react';
import IconGoal from './special-icons/goal.svg?react';
import IconGoogle from './special-icons/google.svg?react';
Expand Down Expand Up @@ -84,5 +85,6 @@ export {
IconFCM,
IconNoData,
IconLogoutConfirm,
IconDelete
IconDelete,
IconFlagConnected
};
6 changes: 6 additions & 0 deletions ui/dashboard/src/@icons/special-icons/flag-connected.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions ui/dashboard/src/@queries/user-segments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
userSegmentsFetcher,
UserSegmentsFetcherParams
} from '@api/user-segment';
import { QueryClient, useQuery, useQueryClient } from '@tanstack/react-query';
import type { UserSegmentCollection, QueryOptionsRespond } from '@types';

type QueryOptions = QueryOptionsRespond<UserSegmentCollection> & {
params?: UserSegmentsFetcherParams;
};

export const USER_SEGMENTS_QUERY_KEY = 'user-segments';

export const useQueryUserSegments = (options?: QueryOptions) => {
const { params, ...queryOptions } = options || {};
const query = useQuery({
queryKey: [USER_SEGMENTS_QUERY_KEY, params],
queryFn: async () => {
return userSegmentsFetcher(params);
},
...queryOptions
});
return query;
};

export const usePrefetchUserSegments = (options?: QueryOptions) => {
const { params, ...queryOptions } = options || {};
const queryClient = useQueryClient();
queryClient.prefetchQuery({
queryKey: [USER_SEGMENTS_QUERY_KEY, params],
queryFn: async () => {
return userSegmentsFetcher(params);
},
...queryOptions
});
};

export const prefetchUserSegments = (
queryClient: QueryClient,
options?: QueryOptions
) => {
const { params, ...queryOptions } = options || {};
queryClient.prefetchQuery({
queryKey: [USER_SEGMENTS_QUERY_KEY, params],
queryFn: async () => {
return userSegmentsFetcher(params);
},
...queryOptions
});
};

export const invalidateUserSegments = (queryClient: QueryClient) => {
queryClient.invalidateQueries({
queryKey: [USER_SEGMENTS_QUERY_KEY]
});
};
4 changes: 3 additions & 1 deletion ui/dashboard/src/@types/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export type OrderBy =
| 'STATE'
| 'LAST_SEEN'
| 'CREATOR_EMAIL'
| 'ENVIRONMENT';
| 'ENVIRONMENT'
| 'CONNECTIONS'
| 'USERS';

export type OrderDirection = 'ASC' | 'DESC';

Expand Down
1 change: 1 addition & 0 deletions ui/dashboard/src/@types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './api-key';
export * from './notification';
export * from './push';
export * from './tag';
export * from './user-segment';
Loading

0 comments on commit bd8710e

Please sign in to comment.