Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add tags in framework #2795

Merged
merged 1 commit into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions app/components/selections/FrameworkTagSelectInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React, { useMemo, useCallback } from 'react';
import {
Button,
BadgeInput,
BadgeInputProps,
} from '@the-deep/deep-ui';
import { useQuery, gql } from '@apollo/client';

import {
FrameworkTagOptionsQuery,
FrameworkTagOptionsQueryVariables,
} from '#generated/types';

import styles from './styles.css';

const FRAMEWORK_TAGS = gql`
query FrameworkTagOptions(
$page: Int,
$pageSize: Int,
) {
analysisFrameworkTags(
page: $page,
pageSize: $pageSize,
) {
page
pageSize
results {
description
id
title
icon {
name
url
}
}
totalCount
}
}
`;

const PAGE_SIZE = 10;

type BasicFrameworkTag = NonNullable<NonNullable<NonNullable<FrameworkTagOptionsQuery>['analysisFrameworkTags']>['results']>[number];
const keySelector = (item: BasicFrameworkTag) => item.id;
const labelSelector = (item: BasicFrameworkTag) => item.title;
const titleSelector = (item: BasicFrameworkTag) => item.description;
function iconSelector(item: BasicFrameworkTag) {
if (!item.icon?.url) {
return undefined;
}
return (
<img
className={styles.icon}
src={item.icon.url}
alt={item.icon.url}
/>
);
}

type Props<N extends string> = Omit<
BadgeInputProps<BasicFrameworkTag, N, string>,
'options' | 'keySelector' | 'labelSelector'
>;

function FrameworkTagSelectInput<N extends string>(
props: Props<N>,
) {
const variables = useMemo(() => ({
page: 1,
pageSize: PAGE_SIZE,
}), []);

const {
data,
fetchMore,
} = useQuery<FrameworkTagOptionsQuery, FrameworkTagOptionsQueryVariables>(
FRAMEWORK_TAGS,
{
variables,
},
);

const handleShowMoreClick = useCallback(() => {
fetchMore({
variables: {
...variables,
page: (data?.analysisFrameworkTags?.page ?? 1) + 1,
},
updateQuery: (previousResult, { fetchMoreResult }) => {
if (!previousResult.analysisFrameworkTags) {
return previousResult;
}

const oldFrameworkTags = previousResult.analysisFrameworkTags;
const newFrameworkTags = fetchMoreResult?.analysisFrameworkTags;

if (!newFrameworkTags) {
return previousResult;
}

return ({
...previousResult,
analysisFrameworkTags: {
...newFrameworkTags,
results: [
...(oldFrameworkTags.results ?? []),
...(newFrameworkTags.results ?? []),
],
},
});
},
});
}, [
data?.analysisFrameworkTags?.page,
fetchMore,
variables,
]);

return (
<>
<BadgeInput
{...props}
keySelector={keySelector}
labelSelector={labelSelector}
titleSelector={titleSelector}
iconSelector={iconSelector}
options={data?.analysisFrameworkTags?.results ?? undefined}
smallButtons
/>
{(data?.analysisFrameworkTags?.totalCount ?? 0)
> (data?.analysisFrameworkTags?.results ?? []).length && (
<Button
onClick={handleShowMoreClick}
name={undefined}
spacing="compact"
variant="transparent"
>
Show more tags
</Button>
)}
</>
);
}

export default FrameworkTagSelectInput;
5 changes: 5 additions & 0 deletions app/components/selections/FrameworkTagSelectInput/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.icon {
width: auto;
height: 1rem;
object-fit: contain;
}
27 changes: 27 additions & 0 deletions app/views/ProjectEdit/Framework/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import {
Button,
Container,
Checkbox,
Kraken,
ListView,
Message,
Expand All @@ -29,6 +30,7 @@ import {
import SmartButtonLikeLink from '#base/components/SmartButtonLikeLink';
import useDebouncedValue from '#hooks/useDebouncedValue';
import ProjectContext from '#base/context/ProjectContext';
import FrameworkTagSelectInput from '#components/selections/FrameworkTagSelectInput';
import { isFiltered } from '#utils/common';
import routes from '#base/configs/routes';
import _ts from '#ts';
Expand All @@ -49,16 +51,20 @@ const frameworkKeySelector = (d: FrameworkType) => d.id;
export const PROJECT_FRAMEWORKS = gql`
query ProjectAnalysisFrameworks(
$isCurrentUserMember: Boolean,
$tags: [ID!],
$search: String,
$recentlyUsed: Boolean,
$page: Int,
$pageSize: Int,
$createdBy: [ID!],
) {
analysisFrameworks(
search: $search,
isCurrentUserMember: $isCurrentUserMember
recentlyUsed: $recentlyUsed,
page: $page,
pageSize: $pageSize,
tags: $tags,
createdBy: $createdBy,
) {
results {
Expand Down Expand Up @@ -133,7 +139,9 @@ const relatedToMeLabelSelector = (d: Option) => d.label;

type FormType = {
relatedToMe?: 'true' | 'false';
recentlyUsed: boolean;
search: string;
tag?: string;
};

type FormSchema = ObjectSchema<PartialForm<FormType>>;
Expand All @@ -142,12 +150,16 @@ type FormSchemaFields = ReturnType<FormSchema['fields']>
const schema: FormSchema = {
fields: (): FormSchemaFields => ({
search: [],
tag: [],
recentlyUsed: [],
relatedToMe: [requiredCondition],
}),
};

const defaultFormValue: PartialForm<FormType> = {
relatedToMe: 'true',
recentlyUsed: false,
tag: undefined,
search: '',
};

Expand Down Expand Up @@ -183,12 +195,16 @@ function ProjectFramework(props: Props) {
const analysisFrameworkVariables = useMemo(() => (
{
isCurrentUserMember: delayedValue.relatedToMe === 'true' ? true : undefined,
tags: delayedValue.tag ? [delayedValue.tag] : undefined,
recentlyUsed: delayedValue.recentlyUsed,
search: delayedValue.search,
page: 1,
pageSize: PAGE_SIZE,
}
), [
delayedValue.relatedToMe,
delayedValue.recentlyUsed,
delayedValue.tag,
delayedValue.search,
]);

Expand Down Expand Up @@ -287,6 +303,17 @@ function ProjectFramework(props: Props) {
value={value.search}
placeholder={_ts('projectEdit', 'searchLabel')}
/>
<Checkbox
name="recentlyUsed"
value={value?.recentlyUsed}
label="Recently Used"
onChange={setFieldValue}
/>
<FrameworkTagSelectInput
name="tag"
onChange={setFieldValue}
value={value.tag}
/>
</div>
<div className={styles.bottomContent}>
<ListView
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"@mapbox/mapbox-gl-draw": "^1.3.0",
"@sentry/react": "^6.16.0",
"@sentry/tracing": "^6.16.0",
"@the-deep/deep-ui": "^2.0.3",
"@the-deep/deep-ui": "^2.0.6",
"@togglecorp/fujs": "^2.0.0",
"@togglecorp/re-map": "^0.1.3",
"@togglecorp/toggle-form": "^1.2.2-beta.2",
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3172,10 +3172,10 @@
content-type "^1.0.5"
tslib "^2.6.1"

"@the-deep/deep-ui@^2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@the-deep/deep-ui/-/deep-ui-2.0.3.tgz#9f0ec63be17801085776e91f107b2da8210396ed"
integrity sha512-XBTi8fx1LSMze0y5LPC04LyY+gZrRFw440+7LM84NF2xSCvPqwQpo5aGjUUcShRD4fozyk7+nnfQ4VYBETzccw==
"@the-deep/deep-ui@^2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@the-deep/deep-ui/-/deep-ui-2.0.6.tgz#b798b5be0a23867677374f967d40c9cd88ec5d2e"
integrity sha512-F9EPrYmS4c1KpbO4sMeH+VUDergj/OCdu313LCuoA73cQW3RB9fPpWFtDmRslWjGCMs7WrW8ndlMDn39AOKA9w==
dependencies:
"@tanem/svg-injector" "^10.1.14"
"@togglecorp/fujs" "^1.9.3"
Expand Down