+
+
+ Show List of Completed Work
+
+ Connect to Dework to show a list of tasks each contributor has
+ done during an epoch
+
+
+
+ {deworkOrganizationId ? (
+
}
+ onClick={() => setDeworkOrganizationId(null)}
+ >
+ Disconnect Dework
+
+ ) : (
+
}
+ href={`${DEWORK_APP_INSTALL_URL}?redirect=${
+ window.location.origin
+ }${getDeworkCallbackPath()}`}
+ >
+ Connect Dework
+
+ )}
+
Discord Webhook
{allowEdit && (
diff --git a/src/pages/DeworkCallbackPage/DeworkCallbackPage.tsx b/src/pages/DeworkCallbackPage/DeworkCallbackPage.tsx
new file mode 100644
index 0000000000..8e39c4dd49
--- /dev/null
+++ b/src/pages/DeworkCallbackPage/DeworkCallbackPage.tsx
@@ -0,0 +1,37 @@
+import { FC, useCallback, useEffect, useMemo } from 'react';
+
+import { useHistory, useLocation } from 'react-router-dom';
+
+import { useApiAdminCircle } from 'hooks';
+import { useCircle, useSelectedCircle } from 'recoilState';
+import { getCirclesPath } from 'routes/paths';
+
+export const DeworkCallbackPage: FC = () => {
+ const history = useHistory();
+ const { search } = useLocation();
+ const deworkOrganizationId = useMemo(
+ () => new URLSearchParams(search).get('dework_organization_id'),
+ [search]
+ );
+
+ const { circleId } = useSelectedCircle();
+ const { circle } = useCircle(circleId);
+ const { updateCircle } = useApiAdminCircle(circleId);
+
+ const updateDeworkOrganizationId = useCallback(async () => {
+ if (deworkOrganizationId) {
+ await updateCircle({
+ ...circle,
+ token_name: circle.tokenName,
+ update_webhook: 0,
+ dework_organization_id: deworkOrganizationId,
+ });
+ }
+
+ history.replace(getCirclesPath());
+ }, []);
+ useEffect(() => {
+ updateDeworkOrganizationId();
+ }, []);
+ return null;
+};
diff --git a/src/recoilState/app.ts b/src/recoilState/app.ts
index 8670b1aaa9..eae2271443 100644
--- a/src/recoilState/app.ts
+++ b/src/recoilState/app.ts
@@ -10,6 +10,7 @@ import {
useRecoilValueLoadable,
} from 'recoil';
+import { DEWORK_API_URL } from 'config/env';
import { getApiService } from 'services/api';
import { extraProfile } from 'utils/modelExtenders';
import { getSelfIdProfile } from 'utils/selfIdHelpers';
@@ -398,3 +399,74 @@ export const useProfile = (address: string) =>
export const useEpochsStatus = (circleId: number) =>
useRecoilValue(rCircleEpochsStatus(circleId));
+
+interface ICircleTask {
+ id: string;
+ name: string;
+ permalink: string;
+ assignees: {
+ id: string;
+ threepids: {
+ source: string;
+ threepid: string;
+ }[];
+ }[];
+}
+
+export const rSelectedCircleTasks = selector
({
+ key: 'rSelectedCircleTasks',
+ get: async ({ get }) => {
+ const circle = get(rSelectedCircle);
+ if (!circle.circle.dework_organization_id) return [];
+ return fetch(`${DEWORK_API_URL}/graphql`, {
+ // return fetch(`https://api.demo.dework.xyz/graphql`, {
+ method: 'POST',
+ headers: { 'content-type': 'application/json' },
+ body: JSON.stringify({
+ query: `
+ query CoordinapeDeworkTasksQuery($organizationId:UUID!) {
+ tasks: getTasks(input:{
+ organizationIds: [$organizationId]
+ statuses: [DONE]
+ }) {
+ id
+ name
+ permalink
+ assignees {
+ id
+ threepids {
+ source
+ threepid
+ }
+ }
+ }
+ }
+ `,
+ variables: { organizationId: circle.circle.dework_organization_id },
+ }),
+ })
+ .then(res => res.json())
+ .then(json => json.data.tasks);
+ },
+});
+
+export const rProfileTasks = selectorFamily({
+ key: 'rProfileTasks',
+ get:
+ (address: string) =>
+ ({ get }) => {
+ const allTasks = get(rSelectedCircleTasks);
+ return allTasks.filter(task =>
+ task.assignees.some(user =>
+ user.threepids.some(
+ t =>
+ t.source === 'metamask' &&
+ t.threepid.toLowerCase() === address.toLowerCase()
+ )
+ )
+ );
+ },
+});
+
+export const useProfileTasks = (address: string) =>
+ useRecoilValue(rProfileTasks(address));
diff --git a/src/routes/paths.ts b/src/routes/paths.ts
index 5d997b082b..dbf7e106a2 100644
--- a/src/routes/paths.ts
+++ b/src/routes/paths.ts
@@ -50,6 +50,7 @@ export const getCirclesPath = () => '/admin/circles';
export const getCreateCirclePath = () => APP_PATH_CREATE_CIRCLE;
export const getProfilePath = ({ address }: { address: string }) =>
`/profile/${address}`;
+export const getDeworkCallbackPath = () => `/dework-callback`;
interface INavItem {
label: string;
diff --git a/src/routes/routes.tsx b/src/routes/routes.tsx
index 994c07a00b..6702ff5a52 100644
--- a/src/routes/routes.tsx
+++ b/src/routes/routes.tsx
@@ -6,6 +6,7 @@ import AdminPage from 'pages/AdminPage';
import AllocationPage from 'pages/AllocationPage';
import CreateCirclePage from 'pages/CreateCirclePage';
import DefaultPage from 'pages/DefaultPage';
+import { DeworkCallbackPage } from 'pages/DeworkCallbackPage/DeworkCallbackPage';
import HistoryPage from 'pages/HistoryPage';
import OverviewPage from 'pages/OverviewPage';
import ProfilePage from 'pages/ProfilePage';
@@ -126,6 +127,12 @@ const LoggedInRoutes = () => {
path={paths.getCirclesPath()}
component={AdminPage}
/>
+
diff --git a/src/types/api.circle.d.ts b/src/types/api.circle.d.ts
index ef0150881b..ff40fb52e0 100644
--- a/src/types/api.circle.d.ts
+++ b/src/types/api.circle.d.ts
@@ -28,6 +28,7 @@ export interface IApiCircle {
protocol_id: number;
protocol: IProtocol;
auto_opt_out: boolean;
+ dework_organization_id: string | null;
}
export interface ICircle extends IApiCircle {
diff --git a/src/types/api.d.ts b/src/types/api.d.ts
index 3c95afb41e..d391f1e48e 100644
--- a/src/types/api.d.ts
+++ b/src/types/api.d.ts
@@ -60,6 +60,7 @@ export interface PutCirclesParam {
only_giver_vouch: boolean;
team_selection: boolean;
auto_opt_out: boolean;
+ dework_organization_id: string | null;
}
export interface CreateCircleParam {