Skip to content

Commit

Permalink
feat(ui): show which flag the segment user is using when deleting or …
Browse files Browse the repository at this point in the history
…updating it (#272)
  • Loading branch information
bimalgrg519 authored Jun 26, 2023
1 parent 999ba67 commit 0ea2d1a
Show file tree
Hide file tree
Showing 9 changed files with 482 additions and 209 deletions.
9 changes: 6 additions & 3 deletions ui/web-v2/apps/admin/src/assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,14 +386,14 @@
"segment.action.download": "Download user list",
"segment.add.header.description": "User segment allows you to manage all user targets for a single feature flag variation. You can use it to make changes to a large number of users or to test beta features on a small number of users.",
"segment.add.header.title": "Create a segment",
"segment.confirm.delete.cannotDelete": "The {segmentName} segment is being used, and you cannot delete it.",
"segment.confirm.delete.description": "The {segmentName} segment will be deleted permanently.",
"segment.confirm.delete.cannotDelete": "The {segmentName} segment can't be deleted because {length} {length, plural, one {flag is} other {flags are}} using it.",
"segment.confirm.delete.description": "The {segmentName} segment will be deleted permanently.",
"segment.confirm.delete.title": "Delete segment",
"segment.enterUserIdsPlaceholder": "Enter IDs separated by commas (E.g., userId1, userId2, userId3)",
"segment.fileUpload.browseFiles": "Browse files",
"segment.fileUpload.fileFormat": "Accepted file type: .csv and .txt (Max size: 2MB)",
"segment.fileUpload.fileMaxSize": "The maximum size of the file is 1MB",
"segment.fileUpload.fileSize": "{fileSize} bytes",
"segment.fileUpload.segmentInUse": "This segment is in use and should remove from the feature flag before updating it",
"segment.fileUpload.unsupportedType": "The file format is not supported",
"segment.fileUpload.uploadInProgress": "The file cannot be updated due to upload in progress",
"segment.fileUpload.userList": "List of user IDs",
Expand All @@ -414,6 +414,9 @@
"segment.status.uploading": "UPLOADING",
"segment.update.header.description": "User segment allows you to manage all user targets for a single feature flag variation. You can use it to make changes to a large number of users or to test beta features on a small number of users.",
"segment.update.header.title": "Update the segment",
"segment.update.userId": "The user ID list can't be updated because {length} {length, plural, one {flag is} other {flags are}} using it. Remove the segment from the flag before updating it.",
"segment.uploading.message": "Segments can't be updated until the user list has been uploaded.",
"segment.uploading.title": "Upload in progress",
"segment.userCount": "users",
"settings.list.header.description": "On this page, you can check all settings for this environment. Select a tab to manage the settings.",
"settings.list.header.title": "Settings",
Expand Down
7 changes: 5 additions & 2 deletions ui/web-v2/apps/admin/src/assets/lang/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -388,14 +388,14 @@
"segment.add.header.title": "ユーザーセグメントの作成",
"segment.list.noData.description": "ユーザーセグメントを作成し、単一のフィーチャーフラグバリエーションのすべてのユーザーターゲットを管理できます。",
"segment.list.noResult.searchKeyword": "名前、説明",
"segment.confirm.delete.cannotDelete": "この「{segmentName}」セグメントが使用中のため、削除できません。",
"segment.confirm.delete.cannotDelete": "この「{segmentName}」セグメントは他の{length}件のフラグに使用中のため、削除できません。",
"segment.confirm.delete.description": "この「{segmentName}」セグメントが永久に削除されます。",
"segment.confirm.delete.title": "ユーザーセグメントの削除",
"segment.enterUserIdsPlaceholder": "IDをカンマで区切って入力する (例: userId1, userId2, userId3)",
"segment.fileUpload.browseFiles": "ファイルを選択",
"segment.fileUpload.fileFormat": "アップロード可能な形式: .csv, .txt (最大サイズ: 2MB)",
"segment.fileUpload.fileMaxSize": "ファイルの最大サイズは2MBです",
"segment.fileUpload.fileSize": "{fileSize} バイト",
"segment.fileUpload.segmentInUse": "このセグメントは使用中のため、ファイルを更新する前にフィーチャーフラグから削除してください",
"segment.fileUpload.unsupportedType": "対応していないファイル形式です",
"segment.fileUpload.uploadInProgress": "アップロード中のためファイルの更新ができません",
"segment.fileUpload.userList": "ユーザーID一覧",
Expand All @@ -414,6 +414,9 @@
"segment.status.uploading": "アップロード中",
"segment.update.header.description": "ユーザーセグメントを使用すると、単一のフィーチャーフラグバリエーションのすべてのユーザーターゲットを管理できます。これを使用して、多数のユーザーターゲットに対して一度に変更を加えることができます。例えば、既存のベータユーザー群に対して、機能をテスト可能になります。",
"segment.update.header.title": "ユーザーセグメントの更新",
"segment.update.userId": "ユーザーID一覧は他の{length}件のフラグに使用中のため、更新できません。このセグメントをフラグから削除してから更新してください。",
"segment.uploading.message": "ユーザー一覧はアップロード中のため、セグメントユーザーの更新ができません。",
"segment.uploading.title": "アップロード中",
"segment.userCount": "ユーザー数",
"settings.list.header.description": "このページでは、環境にあるすべての設定を確認できます。タブを選択すると設定の管理ができます。",
"settings.list.header.title": "設定",
Expand Down
173 changes: 115 additions & 58 deletions ui/web-v2/apps/admin/src/components/SegmentAddForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,27 @@ export interface SegmentAddFormProps {
onCancel: () => void;
}

export enum UserIdListTypes {
BROWSE = 'browse',
USER_IDS = 'userIds',
}

export const userIdListTypes = [
{
id: UserIdListTypes.BROWSE,
title: 'Browse files',
},
{
id: UserIdListTypes.USER_IDS,
title: 'Enter user IDs',
},
];

export const SegmentAddForm: FC<SegmentAddFormProps> = memo(
({ onSubmit, onCancel }) => {
const [selectedUserIdListType, setSelectedUserIdListType] = useState(
UserIdListTypes.BROWSE
);
const { formatMessage: f } = useIntl();
const methods = useFormContext();
const [selectedFile, setSelectedFile] = useState(null);
Expand Down Expand Up @@ -88,7 +107,7 @@ export const SegmentAddForm: FC<SegmentAddFormProps> = memo(
{f(messages.description)}
</span>
<span className="input-label-optional">
{' '}
&nbsp;
{f(messages.input.optional)}
</span>
</label>
Expand All @@ -109,79 +128,117 @@ export const SegmentAddForm: FC<SegmentAddFormProps> = memo(
</div>
</div>
<div className="">
<label htmlFor="file" className="block">
<label className="block">
<span className="input-label">
{f(messages.segment.fileUpload.userList)}
</span>
&nbsp;
<span className="input-label-optional">
{' '}
{f(messages.input.optional)}
</span>
</label>
<div className="mt-1">
<div className="mb-2">
<div
className={classNames(
'relative h-28 rounded-lg border-dashed',
'border-2 border-gray-300 bg-gray-100',
'flex justify-center items-center'
)}
>
<div className="absolute">
<div className="flex flex-col items-center ">
<div className="text-gray-500">
<FileUploadIcon />
</div>
<span className="block text-gray-500">
{f(messages.segment.fileUpload.browseFiles)}
</span>
</div>
</div>
<div className="mt-1 flex items-center space-x-4">
{userIdListTypes.map(({ id, title }) => (
<div key={id} className="flex items-center">
<input
{...register('file')}
id="file"
name="file"
type="file"
className="input-file"
onInput={onFileInput}
accept=".csv,.txt"
disabled={isSubmitted}
id={id}
name="notification-method"
type="radio"
className="h-4 w-4 border-gray-300 text-primary focus:ring-primary"
defaultChecked={id === selectedUserIdListType}
onClick={() => setSelectedUserIdListType(id)}
/>
<label
htmlFor={id}
className="ml-3 block text-sm leading-6"
>
{title}
</label>
</div>
<div className="flex text-gray-400 my-2">
{f(messages.segment.fileUpload.fileFormat)}
))}
</div>
<div className="mt-2">
{selectedUserIdListType === UserIdListTypes.USER_IDS ? (
<div className="mt-1">
<textarea
{...register('userIds')}
id="userIds"
name="userIds"
rows={4}
className="input-text w-full"
placeholder={f(
messages.segment.enterUserIdsPlaceholder
)}
/>
</div>
</div>
{selectedFile && (
<div
className={classNames(
'h-14 rounded-lg border-dashed',
'border-2 border-gray-300',
'flex'
)}
>
<div className="flex items-center ml-3">
<div className="text-gray-300">
<FilePresentIcon />
) : (
<div>
<div className="mb-2">
<div
className={classNames(
'relative h-[90px] rounded-lg border-dashed',
'border-2 border-gray-300 bg-gray-100',
'flex justify-center items-center'
)}
>
<div className="absolute">
<div className="flex flex-col items-center ">
<div className="text-gray-500">
<FileUploadIcon />
</div>
<span className="block text-gray-500">
{f(messages.segment.fileUpload.browseFiles)}
</span>
</div>
</div>
<input
{...register('file')}
id="file"
name="file"
type="file"
className="input-file"
onInput={onFileInput}
accept=".csv,.txt"
disabled={isSubmitted}
/>
</div>
<div className="ml-3">
<p className="text-base text-sm text-gray-700 w-96 truncate ...">
{selectedFile.name}
</p>
<p className="text-xs text-gray-500">
{f(messages.segment.fileUpload.fileSize, {
fileSize: selectedFile.size.toLocaleString(),
})}
</p>
<div className="flex text-gray-400 my-2">
{f(messages.segment.fileUpload.fileFormat)}
</div>
</div>
{selectedFile && (
<div
className={classNames(
'h-14 rounded-lg border-dashed',
'border-2 border-gray-300',
'flex'
)}
>
<div className="flex items-center ml-3">
<div className="text-gray-300">
<FilePresentIcon />
</div>
<div className="ml-3">
<p className="text-base text-gray-700 w-96 truncate ...">
{selectedFile.name}
</p>
<p className="text-xs text-gray-500">
{f(messages.segment.fileUpload.fileSize, {
fileSize:
selectedFile.size.toLocaleString(),
})}
</p>
</div>
</div>
</div>
)}
<p className="input-error">
{errors.file && (
<span role="alert">{errors.file.message}</span>
)}
</p>
</div>
)}
<p className="input-error">
{errors.file && (
<span role="alert">{errors.file.message}</span>
)}
</p>
</div>
</div>
</div>
Expand Down
48 changes: 42 additions & 6 deletions ui/web-v2/apps/admin/src/components/SegmentDeleteDialog/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { PAGE_PATH_FEATURES, PAGE_PATH_ROOT } from '@/constants/routing';
import { useCurrentEnvironment } from '@/modules/me';
import { Dialog, Transition } from '@headlessui/react';
import {
InformationCircleIcon,
ExclamationCircleIcon,
} from '@heroicons/react/solid';
import { Fragment, FC } from 'react';
import { useIntl } from 'react-intl';
import { Link } from 'react-router-dom';

import { messages } from '../../lang/messages';
import { Segment } from '../../proto/feature/segment_pb';
Expand All @@ -20,6 +27,8 @@ export const SegmentDeleteDialog: FC<SegmentDeleteDialogProps> = ({
onClose,
}) => {
const { formatMessage: f } = useIntl();
const currentEnvironment = useCurrentEnvironment();

return (
<Modal open={open} onClose={onClose}>
<Dialog.Title
Expand All @@ -30,15 +39,42 @@ export const SegmentDeleteDialog: FC<SegmentDeleteDialogProps> = ({
</Dialog.Title>
<div className="mt-2">
{segment && segment.isInUseStatus ? (
<p className="text-sm text-gray-700">
{f(messages.segment.confirm.cannotDelete, {
segmentName: `${segment.name}`,
})}
</p>
<div className="rounded-md bg-yellow-50 p-4 mt-2">
<div className="flex">
<div className="flex-shrink-0">
<ExclamationCircleIcon
className="h-5 w-5 text-yellow-400"
aria-hidden="true"
/>
</div>
<div className="ml-3 flex-1">
<p className="text-sm text-yellow-700">
{f(messages.segment.confirm.cannotDelete, {
segmentName: <strong>{`${segment.name}`}</strong>,
length: segment.featuresList.length,
})}
</p>
<div className="mt-2 text-sm text-yellow-700">
<ul className="list-disc space-y-1 pl-5">
{segment.featuresList.map((feature) => (
<li key={feature.id}>
<Link
className="link text-left"
to={`${PAGE_PATH_ROOT}${currentEnvironment.id}${PAGE_PATH_FEATURES}/${feature.id}`}
>
<p className="truncate w-60">{feature.name}</p>
</Link>
</li>
))}
</ul>
</div>
</div>
</div>
</div>
) : (
<p className="text-sm text-red-500">
{f(messages.segment.confirm.deleteDescription, {
segmentName: `${segment && segment.name}`,
segmentName: <strong>{segment && segment.name}</strong>,
})}
</p>
)}
Expand Down
7 changes: 5 additions & 2 deletions ui/web-v2/apps/admin/src/components/SegmentList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,13 @@ export const SegmentList: FC<SegmentListProps> = memo(
);
}
};
const createMenuItems = (): Array<MenuItem> => {
const createMenuItems = (includedUserCount: Number): Array<MenuItem> => {
const items: Array<MenuItem> = [];
items.push({
action: MenuActions.DOWNLOAD,
name: intl.formatMessage(messages.segment.action.download),
iconElement: <MUCloudDownloadIcon />,
disabled: includedUserCount === 0,
});
items.push({
action: MenuActions.DELETE,
Expand Down Expand Up @@ -206,7 +207,9 @@ export const SegmentList: FC<SegmentListProps> = memo(
return;
}
}}
menuItems={createMenuItems()}
menuItems={createMenuItems(
segment.includedUserCount
)}
/>
</td>
)}
Expand Down
Loading

0 comments on commit 0ea2d1a

Please sign in to comment.