From 797052301a1a63f9a32b2a8ce2cdbac650b6f899 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 10 Oct 2022 17:59:28 +0300 Subject: [PATCH] Member autocomplete --- package.json | 1 + .../InputHandleAutocomplete.module.scss | 5 + .../InputHandleAutocomplete.tsx | 94 +++++++++++++++++++ src-ts/lib/member-autocomplete/index.ts | 1 + .../input-handle-functions.ts | 23 +++++ .../game-lib/game-badge.model.ts | 6 ++ .../AwardedMembersTab.module.scss | 3 + .../AwardedMembersTab/AwardedMembersTab.tsx | 19 ++++ .../badge-detail/AwardedMembersTab/index.ts | 1 + .../badge-detail/BadgeDetailPage.module.scss | 8 ++ .../pages/badge-detail/BadgeDetailPage.tsx | 36 +++++-- .../BatchAwardTab/BatchAwardTab.module.scss | 3 + .../BatchAwardTab/BatchAwardTab.tsx | 13 +++ .../pages/badge-detail/BatchAwardTab/index.ts | 1 + .../ManualAwardTab/ManualAwardTab.module.scss | 23 +++++ .../ManualAwardTab/ManualAwardTab.tsx | 52 ++++++++++ .../badge-detail/ManualAwardTab/index.ts | 1 + yarn.lock | 2 +- 18 files changed, 282 insertions(+), 10 deletions(-) create mode 100644 src-ts/lib/member-autocomplete/InputHandleAutocomplete.module.scss create mode 100644 src-ts/lib/member-autocomplete/InputHandleAutocomplete.tsx create mode 100644 src-ts/lib/member-autocomplete/index.ts create mode 100644 src-ts/lib/member-autocomplete/input-handle-functions.ts create mode 100644 src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/AwardedMembersTab.module.scss create mode 100644 src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/AwardedMembersTab.tsx create mode 100644 src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/index.ts create mode 100644 src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/BatchAwardTab.module.scss create mode 100644 src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/BatchAwardTab.tsx create mode 100644 src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/index.ts create mode 100644 src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/ManualAwardTab.module.scss create mode 100644 src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/ManualAwardTab.tsx create mode 100644 src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/index.ts diff --git a/package.json b/package.json index 556717fd9..7913d0c3a 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "moment": "^2.29.3", "moment-timezone": "^0.5.34", "prop-types": "^15.8.1", + "qs": "^6.11.0", "rc-checkbox": "^2.3.2", "react": "^17.0.2", "react-apexcharts": "^1.4.0", diff --git a/src-ts/lib/member-autocomplete/InputHandleAutocomplete.module.scss b/src-ts/lib/member-autocomplete/InputHandleAutocomplete.module.scss new file mode 100644 index 000000000..3ffa6b159 --- /dev/null +++ b/src-ts/lib/member-autocomplete/InputHandleAutocomplete.module.scss @@ -0,0 +1,5 @@ +@import "../styles/variables/palette"; + +.memberSelect { + color: $black-60; +} diff --git a/src-ts/lib/member-autocomplete/InputHandleAutocomplete.tsx b/src-ts/lib/member-autocomplete/InputHandleAutocomplete.tsx new file mode 100644 index 000000000..03a8810d5 --- /dev/null +++ b/src-ts/lib/member-autocomplete/InputHandleAutocomplete.tsx @@ -0,0 +1,94 @@ +import { FC, FocusEvent } from 'react' +import { MultiValue, StylesConfig } from 'react-select' +// tslint:disable-next-line: no-submodule-imports +import AsyncSelect from 'react-select/async' + +import { InputWrapper } from '../form/form-groups/form-input/input-wrapper' + +import { membersAutocompete, MembersAutocompeteResult } from './input-handle-functions' +import styles from './InputHandleAutocomplete.module.scss' + +export interface InputHandleAutocompleteProps { + readonly className?: string + readonly dirty?: boolean + readonly disabled?: boolean + readonly error?: string + readonly hideInlineErrors?: boolean + readonly hint?: string + readonly label?: string | JSX.Element + readonly name: string + readonly onBlur?: (event: FocusEvent) => void + readonly onChange: (newValue: Array) => void + readonly placeholder?: string + readonly tabIndex: number + readonly value?: Array +} + +const InputHandleAutocomplete: FC = (props: InputHandleAutocompleteProps) => { + const customStyles: StylesConfig = { + control: (provided) => ({ + ...provided, + border: 'none', + }), + input: (provided) => ({ + ...provided, + color: 'inherit', + fontSize: 16, + }), + multiValue: (provided) => ({ + ...provided, + borderRadius: 50, + }), + multiValueLabel: (provided) => ({ + ...provided, + fontSize: 12, + }), + option: (provided) => ({ + ...provided, + borderBottom: '1px solid #E9E9E9', + color: 'inherit', + fontSize: 16, + fontWeight: 400, + padding: 16, + }), + placeholder: (provided) => ({ + ...provided, + color: 'inherit', + fontSize: 16, + fontWeight: 400, + }), + valueContainer: (provided) => ({ + ...provided, + padding: 0, + }), + } + + return ( + + handle} + getOptionValue={({ userId }) => userId} + isMulti + key={props.value?.length} + loadOptions={membersAutocompete} + styles={customStyles} + placeholder={props.placeholder} + onBlur={props.onBlur} + onChange={(newValue: MultiValue) => props.onChange(newValue as Array)} + value={props.value} + isDisabled={props.disabled} + /> + + ) +} + +export default InputHandleAutocomplete diff --git a/src-ts/lib/member-autocomplete/index.ts b/src-ts/lib/member-autocomplete/index.ts new file mode 100644 index 000000000..92b750f67 --- /dev/null +++ b/src-ts/lib/member-autocomplete/index.ts @@ -0,0 +1 @@ +export { default as InputHandleAutocomplete } from './InputHandleAutocomplete' diff --git a/src-ts/lib/member-autocomplete/input-handle-functions.ts b/src-ts/lib/member-autocomplete/input-handle-functions.ts new file mode 100644 index 000000000..7fbd66afa --- /dev/null +++ b/src-ts/lib/member-autocomplete/input-handle-functions.ts @@ -0,0 +1,23 @@ +import qs from 'qs' + +import { xhrGetAsync } from '..' +import { EnvironmentConfig } from '../../config' + +export interface MembersAutocompeteQuery { + term: string +} + +export interface MembersAutocompeteResult { + firstName: string + handle: string + lastName: string + userId: string +} + +export async function membersAutocompete(term: string): Promise> { + const query: MembersAutocompeteQuery = { + term, + } + + return xhrGetAsync(`${EnvironmentConfig.API.V5}/members/autocomplete?${qs.stringify(query)}`) +} diff --git a/src-ts/tools/gamification-admin/game-lib/game-badge.model.ts b/src-ts/tools/gamification-admin/game-lib/game-badge.model.ts index 7c4c719b2..f4de99dd3 100644 --- a/src-ts/tools/gamification-admin/game-lib/game-badge.model.ts +++ b/src-ts/tools/gamification-admin/game-lib/game-badge.model.ts @@ -6,5 +6,11 @@ export interface GameBadge { badge_name: string badge_status: string id: string + member_badges?: Array<{ + awarded_at: string, + awarded_by: string, + user_handle: string, + user_id: string, + }> organization_id: string } diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/AwardedMembersTab.module.scss b/src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/AwardedMembersTab.module.scss new file mode 100644 index 000000000..36bd71e18 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/AwardedMembersTab.module.scss @@ -0,0 +1,3 @@ +.tabWrap { + display: flex; +} \ No newline at end of file diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/AwardedMembersTab.tsx b/src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/AwardedMembersTab.tsx new file mode 100644 index 000000000..faea0f664 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/AwardedMembersTab.tsx @@ -0,0 +1,19 @@ +import { FC } from 'react' + +import { GameBadge } from '../../../game-lib' + +import styles from './AwardedMembersTab.module.scss' + +export interface AwardedMembersTabProps { + awardedMembers?: GameBadge['member_badges'] +} + +const AwardedMembersTab: FC = (props: AwardedMembersTabProps) => { + return ( +
+ +
+ ) +} + +export default AwardedMembersTab diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/index.ts b/src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/index.ts new file mode 100644 index 000000000..e9063473a --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/badge-detail/AwardedMembersTab/index.ts @@ -0,0 +1 @@ +export * from './AwardedMembersTab' diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/BadgeDetailPage.module.scss b/src-ts/tools/gamification-admin/pages/badge-detail/BadgeDetailPage.module.scss index 35cd18f67..01e18d2e6 100644 --- a/src-ts/tools/gamification-admin/pages/badge-detail/BadgeDetailPage.module.scss +++ b/src-ts/tools/gamification-admin/pages/badge-detail/BadgeDetailPage.module.scss @@ -131,4 +131,12 @@ $badgePreviewImage: 72px; } } } + + .activeTabElement { + margin-top: $space-xxxxl; + + @include ltemd { + margin-top: $space-md; + } + } } \ No newline at end of file diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/BadgeDetailPage.tsx b/src-ts/tools/gamification-admin/pages/badge-detail/BadgeDetailPage.tsx index 70ac1e1c2..aa79d15f9 100644 --- a/src-ts/tools/gamification-admin/pages/badge-detail/BadgeDetailPage.tsx +++ b/src-ts/tools/gamification-admin/pages/badge-detail/BadgeDetailPage.tsx @@ -9,14 +9,18 @@ import { Breadcrumb, BreadcrumbItemModel, Button, ButtonProps, ContentLayout, Ic import { GamificationConfig } from '../../game-config' import { BadgeDetailPageHandler, GameBadge, useGamificationBreadcrumb, useGetGameBadgeDetails } from '../../game-lib' +import AwardedMembersTab from './AwardedMembersTab/AwardedMembersTab' import { badgeDetailsTabs, BadgeDetailsTabViews } from './badge-details-tabs.config' import { submitRequestAsync as updateBadgeAsync } from './badge-details.functions' import styles from './BadgeDetailPage.module.scss' +import BatchAwardTab from './BatchAwardTab/BatchAwardTab' +import ManualAwardTab from './ManualAwardTab/ManualAwardTab' const md: MarkdownIt = new MarkdownIt({ html: true, - linkify: true, - typographer: true, + // TODO: check with PM ig those are needed? + // linkify: true, + // typographer: true, }) /* tslint:disable:cyclomatic-complexity */ @@ -38,7 +42,9 @@ const BadgeDetailPage: FC = () => { const { hash }: { hash: string } = useLocation() - const activeTab: string = hash === '#award' ? BadgeDetailsTabViews.manualAward : BadgeDetailsTabViews.awardedMembers + const [activeTab, setActiveTab]: [string, Dispatch>] = useState( + hash === '#award' ? BadgeDetailsTabViews.manualAward : BadgeDetailsTabViews.awardedMembers + ) const [tabs]: [ ReadonlyArray, @@ -118,19 +124,15 @@ const BadgeDetailPage: FC = () => { badgeDetailsHandler.data, ]) - // define the tabs so they can be displayed on various results + // define the tabs so they can be displayed on various tasks const tabsElement: JSX.Element = ( ) - function onChangeTab(active: string): void { - // TODO: implement in GAME-129 - } - function onActivateBadge(): void { // TODO: implement in GAME-127 } @@ -189,6 +191,19 @@ const BadgeDetailPage: FC = () => { } } + // default tab + let activeTabElement: JSX.Element + = + if (activeTab === BadgeDetailsTabViews.manualAward) { + activeTabElement = + } + if (activeTab === BadgeDetailsTabViews.batchAward) { + activeTabElement = + } + + // show page loader if we fetching results if (!badgeDetailsHandler.data && !badgeDetailsHandler.error) { return } @@ -268,6 +283,9 @@ const BadgeDetailPage: FC = () => { {tabsElement} +
+ {activeTabElement} +
) } diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/BatchAwardTab.module.scss b/src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/BatchAwardTab.module.scss new file mode 100644 index 000000000..36bd71e18 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/BatchAwardTab.module.scss @@ -0,0 +1,3 @@ +.tabWrap { + display: flex; +} \ No newline at end of file diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/BatchAwardTab.tsx b/src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/BatchAwardTab.tsx new file mode 100644 index 000000000..d71def501 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/BatchAwardTab.tsx @@ -0,0 +1,13 @@ +import { FC } from 'react' + +import styles from './BatchAwardTab.module.scss' + +const BatchAwardTab: FC = () => { + return ( +
+

Batch Award

+
+ ) +} + +export default BatchAwardTab diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/index.ts b/src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/index.ts new file mode 100644 index 000000000..1cb3ea691 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/badge-detail/BatchAwardTab/index.ts @@ -0,0 +1 @@ +export * from './BatchAwardTab' diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/ManualAwardTab.module.scss b/src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/ManualAwardTab.module.scss new file mode 100644 index 000000000..9e069b176 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/ManualAwardTab.module.scss @@ -0,0 +1,23 @@ +@import "../../../../../lib/styles/variables/palette"; +@import "../../../../../lib/styles/includes"; + +.tabWrap { + display: flex; + flex-direction: column; + + .manualFormWrap { + display: grid; + grid-template-columns: 1fr 1fr; + gap: $space-xxxxl; + margin-top: $space-xxl; + + @include ltemd { + grid-template-columns: 1fr; + } + + .manualForm { + display: flex; + flex-direction: column; + } + } +} \ No newline at end of file diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/ManualAwardTab.tsx b/src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/ManualAwardTab.tsx new file mode 100644 index 000000000..4864655ad --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/ManualAwardTab.tsx @@ -0,0 +1,52 @@ +import { Dispatch, FC, SetStateAction, useState } from 'react' + +import { Button } from '../../../../../lib' +import { InputHandleAutocomplete } from '../../../../../lib/member-autocomplete' +import { MembersAutocompeteResult } from '../../../../../lib/member-autocomplete/input-handle-functions' +import { GameBadge } from '../../../game-lib' + +import styles from './ManualAwardTab.module.scss' + +export interface ManualAwardTabProps { + awardedMembers?: GameBadge['member_badges'] +} + +const ManualAwardTab: FC = (props: ManualAwardTabProps) => { + + const [selectedMembers, setSelectedMembers]: [Array, Dispatch>>] + = useState>([]) + + function onAward(): void { + setSelectedMembers([]) + } + + return ( +
+

Manual Award

+
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Neque ullamcorper neque sed orci, enim amet, sed.

+
+ +
+
+
+
+
+ ) +} + +export default ManualAwardTab diff --git a/src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/index.ts b/src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/index.ts new file mode 100644 index 000000000..750a3f7a4 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/badge-detail/ManualAwardTab/index.ts @@ -0,0 +1 @@ +export * from './ManualAwardTab' diff --git a/yarn.lock b/yarn.lock index a299bd7e2..1a8c31613 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12798,7 +12798,7 @@ qs@6.10.3: dependencies: side-channel "^1.0.4" -qs@^6.9.4: +qs@^6.11.0, qs@^6.9.4: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==