Skip to content

Commit

Permalink
attestation updates
Browse files Browse the repository at this point in the history
  • Loading branch information
shadrach-tayo committed Nov 7, 2024
1 parent 3d226fb commit ffa050d
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 105 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.53.0",
"react-icons": "^5.3.0",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^2.1.2",
"recharts": "^2.12.7",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use client";

import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
import { listAttestationsQuery, listCommunitiesQuery } from "@/lib/api";
import { useFormState } from "react-dom";
import { createAttestation, updateAttestation } from "@/app/actions";
import { useQuery } from "@tanstack/react-query";
import { listAttestationsQuery } from "@/lib/api";
import { useFormState, useFormStatus } from "react-dom";
import { updateAttestation } from "@/app/actions";
import AttestationForm from "@/components/organisms/forms/attestation-form";
import NotFoundError from "@/app/not-found";
import { getQueryClient } from "@/lib/get-query-client";
Expand All @@ -20,13 +20,12 @@ export default function Page({
const [state, formAction] = useFormState<
ReturnType<typeof updateAttestation>
>(updateAttestation, defaultState);

useFormStatus();
// todo: add skeleton loader
const { data, isLoading } = useQuery(listAttestationsQuery, getQueryClient());
const attestation = data?.find((com) => com.id === parseInt(params.attestationId));

if (!attestation) return <NotFoundError />

return (
<AttestationForm
formAction={(formdata) => {
Expand Down
88 changes: 71 additions & 17 deletions src/components/molecules/CommunityAttestations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,23 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Check, X, Award, Plus, Settings, Lock, Pen } from "lucide-react";
import { RxAccessibility } from "react-icons/rx";
import { cn } from "@/lib/utils";
import {
addEntryAttestation,
attestationQueryOptions,
Community,
listAttestationsQuery,
removeEntryAttestation,
toggleEntryAttestationRequirement,
} from "@/lib/api";
import Link from "next/link";
import { buttonVariants, LoaderButton } from "../custom/LoaderButton";
import { useMutation, useQuery, useSuspenseQuery } from "@tanstack/react-query";
import { DotsHorizontalIcon } from "@radix-ui/react-icons";
import { useRouter } from "next/navigation";
import { Tooltip, TooltipContent, TooltipProvider } from "../ui/tooltip";
import { TooltipTrigger } from "@radix-ui/react-tooltip";

export default function CommunityAttestations({
community,
Expand All @@ -50,26 +54,24 @@ export default function CommunityAttestations({
const router = useRouter();
const [attestationOpen, setAttestationOpen] = useState(false);

const {
data: attestations,
refetch: refetchCommunityAttestations,
} = useQuery(attestationQueryOptions(community.id));
const { data: attestations, refetch: refetchCommunityAttestations } =
useQuery(attestationQueryOptions(community.id));
const { data: allAttestations, refetch: refetchAttestations } =
useSuspenseQuery(listAttestationsQuery);

const addEntryMutation = useMutation({ mutationFn: addEntryAttestation });
const removeEntryMutation = useMutation({
mutationFn: removeEntryAttestation,
});
const toggleEntryMutation = useMutation({
mutationFn: toggleEntryAttestationRequirement,
});

const toggleEntryAttestation = (id: number) => {
if (!attestations) return;
const isExisting =
attestations.find(
(att) =>
att.id === id &&
att.isRequired &&
att.entryAttestationId !== undefined
(att) => att.id === id && att.entryAttestationId !== undefined
) !== undefined;

if (isExisting) {
Expand Down Expand Up @@ -98,11 +100,29 @@ export default function CommunityAttestations({
}
};

const isPending = addEntryMutation.isPending || removeEntryMutation.isPending;
// const hasError = addEntryMutation.isError || removeEntryMutation.isError;
// const error = addEntryMutation.error || removeEntryMutation.error;
const toggleEntryRequirement = (id: number) => {
if (!attestations) return;
const entryAttestation = attestations.find(
(att) => att.attestationId === id && att.entryAttestationId !== undefined
);

if (entryAttestation?.entryAttestationId) {
toggleEntryMutation.mutate(
{
communityId: community.id,
entryId: entryAttestation.entryAttestationId,
},
{
onSuccess() {
refetchAttestations();
refetchCommunityAttestations();
},
}
);
}
};

// console.log("states", { isPending, hasError, error });
const isPending = addEntryMutation.isPending || removeEntryMutation.isPending;

return (
<Card className="mb-6">
Expand Down Expand Up @@ -155,7 +175,7 @@ export default function CommunityAttestations({
)}
</div>
{attestations?.some(
(a) => a.id === attestation.id && a.isRequired
(a) => a.attestationId === attestation.id
) && <Check className="h-4 w-4 text-green-600" />}
</CommandItem>
))}
Expand Down Expand Up @@ -187,6 +207,21 @@ export default function CommunityAttestations({
<div className="flex items-center justify-start gap-1">
<p className="font-medium">{attestation.name}</p>
{attestation.protected && <Lock className="h-3 w-3" />}
{attestation.isRequired && (
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger>
<RxAccessibility className="h-3 w-3" />
</TooltipTrigger>
<TooltipContent>
<p>
This attestation is required strictly required to
join the community
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
<p className="font-light text-muted-foreground text-sm">
By {attestation.communityName}
Expand Down Expand Up @@ -215,16 +250,35 @@ export default function CommunityAttestations({
<span>Edit</span>
<span className="sr-only">Edit</span>
</DropdownMenuItem>
{attestation.isRequired && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() =>
toggleEntryAttestation(attestation.attestationId)
}
>
{attestation.entryAttestationId ? (
<span>
{attestation.communityName === community.name
? "Remove from entry"
: "Delete"}
</span>
) : (
<span>Add to Entry</span>
)}
</DropdownMenuItem>
</>
{attestation.entryAttestationId && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() =>
toggleEntryAttestation(attestation.attestationId)
toggleEntryRequirement(attestation.attestationId)
}
>
Delete
<DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
{attestation.isRequired
? "Mark as Optional"
: "Mark as required"}
</DropdownMenuItem>
</>
)}
Expand Down
6 changes: 3 additions & 3 deletions src/components/molecules/CommunityMembers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { tags } from "@/lib/tags";

type User = {
id: number;
name: string;
name?: string;
organisations: string[];
};

Expand Down Expand Up @@ -153,7 +153,7 @@ export default function CommunityMembers({
<div className="flex items-center">
<Avatar className="h-9 w-9">
<AvatarImage src="/avatars/01.png" alt="Avatar" />
<AvatarFallback>{getNameTag(user.name)}</AvatarFallback>
<AvatarFallback>{getNameTag(user.name ?? '')}</AvatarFallback>
</Avatar>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">
Expand Down Expand Up @@ -215,6 +215,6 @@ export default function CommunityMembers({
}

const getNameTag = (name: string) => {
const split = name.split(" ");
const split = name?.split(" ");
return (split[0]?.[0] ?? "") + (split?.[1]?.[0] ?? "");
};
9 changes: 7 additions & 2 deletions src/components/organisms/forms/attestation-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import React, {
} from "react";
import { getQueryClient } from "@/lib/get-query-client";
import { tags } from "@/lib/tags";
import { useRouter } from "next/navigation";

const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const ACCEPTED_IMAGE_TYPES = [
Expand Down Expand Up @@ -85,6 +86,8 @@ export default function AttestationForm({
defaultValues?: FormValues;
// pending: boolean;
}) {
const router = useRouter();

const form = useForm<FormValues>({
resolver: zodResolver(attestationSchema),
defaultValues,
Expand Down Expand Up @@ -124,14 +127,16 @@ export default function AttestationForm({
>;

useEffect(() => {
console.log("attestationForm", formState);
console.log("attestationForm", formState.ok, form.formState.isLoading);
if (formState?.ok) {
getQueryClient().invalidateQueries({
queryKey: [tags.attestations],
});
getQueryClient().invalidateQueries({ queryKey: [{ type: tags.attestations, id: defaultValues?.communityId }], });
router.back();
// todo: show success toast
}
}, [defaultValues?.communityId, form, formState]);
}, [defaultValues?.communityId, form, formState, router]);

const isProtected = form.watch("protected");
return (
Expand Down
5 changes: 4 additions & 1 deletion src/components/organisms/forms/community-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { createCommunity } from "@/app/actions";
import { useEffect } from "react";
import { getQueryClient } from "@/lib/get-query-client";
import { tags } from "@/lib/tags";
import { useRouter } from "next/navigation";

const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const ACCEPTED_IMAGE_TYPES = [
Expand Down Expand Up @@ -95,6 +96,7 @@ export default function CommunityForm({
control: form.control,
name: "links",
});
const router = useRouter();

function onSubmit(data: FormValues) {
const formData = new FormData();
Expand Down Expand Up @@ -137,8 +139,9 @@ export default function CommunityForm({
if (formState?.ok) {
getQueryClient().invalidateQueries({ queryKey: [tags.communities] });
// todo: show success toast
router.back()
}
}, [form, formState]);
}, [form, formState, router]);

return (
<Layout>
Expand Down
16 changes: 16 additions & 0 deletions src/lib/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,22 @@ export const removeEntryAttestation = async ({
);
};

export const toggleEntryAttestationRequirement = async ({
communityId,
entryId,
}: {
communityId: number;
entryId: number;
}) => {
return fetch(
`${NODES_API_URL}/v1/admin/communities/${communityId}/toggleEntryAttestation/${entryId}`,
{
method: "POST",
credentials: "include",
}
);
};

export const addMember = async ({
communityId,
userId,
Expand Down
Loading

0 comments on commit ffa050d

Please sign in to comment.