Skip to content

Commit

Permalink
Merge branch 'master' into cl893/gameplay-updates
Browse files Browse the repository at this point in the history
  • Loading branch information
cathli66 committed May 1, 2024
2 parents 450f9fd + 2fc02e6 commit 1d3fdd8
Show file tree
Hide file tree
Showing 43 changed files with 1,122 additions and 442 deletions.
7 changes: 7 additions & 0 deletions admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { ServerConnectionContext } from "./components/ServerConnection";
import { ServerDataContext } from "./components/ServerData";
import { AlertModal } from "./components/AlertModal";
import { Groups } from "./components/Groups";
import { Achievements } from "./components/Achievements";

const routes = [
{
Expand All @@ -64,6 +65,12 @@ const routes = [
icon: faLocationDot,
name: "Challenges",
},
{
path: "/achievements",
element: <Achievements />,
icon: faTrophy,
name: "Achievements",
},
{
path: "/users",
element: <Users />,
Expand Down
13 changes: 8 additions & 5 deletions admin/src/all.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ export interface RequestAchievementDataDto {
achievements: string[];
}

export interface RequestAchievementTrackerDataDto {
achievements?: string[];
}

export interface LoginDto {
idToken: string;
noRegister: boolean;
Expand All @@ -121,9 +125,7 @@ export interface RefreshTokenDto {
refreshToken: string;
}

export interface CompletedChallengeDto {
challengeId: string;
}
export interface CompletedChallengeDto {}

export interface ChallengeDto {
id: string;
Expand Down Expand Up @@ -230,7 +232,7 @@ export interface EventTrackerDto {
eventId: string;
isRanked: boolean;
hintsUsed: number;
curChallengeId: string;
curChallengeId?: string;
prevChallenges: PrevChallengeDto[];
}

Expand Down Expand Up @@ -261,7 +263,7 @@ export interface GroupMemberDto {
id: string;
name: string;
points: number;
curChallengeId: string;
curChallengeId?: string;
}

export interface GroupDto {
Expand Down Expand Up @@ -293,6 +295,7 @@ export interface OrganizationDto {
members?: string[];
events?: string[];
managers?: string[];
achivements?: string[];
}

export interface RequestOrganizationDataDto {
Expand Down
287 changes: 287 additions & 0 deletions admin/src/components/Achievements.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
import { useContext, useMemo, useState } from "react";
import { DeleteModal } from "./DeleteModal";
import {
EntryModal,
EntryForm,
NumberEntryForm,
OptionEntryForm,
FreeEntryForm,
DateEntryForm,
} from "./EntryModal";
import { HButton } from "./HButton";
import {
ButtonSizer,
CenterText,
ListCardBody,
ListCardBox,
ListCardButtons,
ListCardDescription,
ListCardTitle,
} from "./ListCard";
import { SearchBar } from "./SearchBar";
import { ServerDataContext } from "./ServerData";

import { compareTwoStrings } from "string-similarity";
import {
AchievementDto,
AchievementTypeDto,
ChallengeLocationDto,
} from "../all.dto";
import { AlertModal } from "./AlertModal";

const locationOptions = [
ChallengeLocationDto.ENG_QUAD,
ChallengeLocationDto.ARTS_QUAD,
ChallengeLocationDto.AG_QUAD,
ChallengeLocationDto.NORTH_CAMPUS,
ChallengeLocationDto.WEST_CAMPUS,
ChallengeLocationDto.COLLEGETOWN,
ChallengeLocationDto.ITHACA_COMMONS,
ChallengeLocationDto.ANY,
];

const achievementOptions = [
AchievementTypeDto.TOTAL_CHALLENGES,
AchievementTypeDto.TOTAL_CHALLENGES_OR_JOURNEYS,
AchievementTypeDto.TOTAL_JOURNEYS,
AchievementTypeDto.TOTAL_POINTS,
];

function AchiemementCard(props: {
achievement: AchievementDto;
onSelect: () => void;
onEdit: () => void;
onDelete: () => void;
}) {
return (
<>
<ListCardBox>
<ListCardTitle>
{props.achievement.name}
<ButtonSizer>
<HButton onClick={props.onSelect} float="right">
{props.achievement.eventId ? "UNLINK EVENT" : "LINK EVENT"}
</HButton>
</ButtonSizer>
</ListCardTitle>
<ListCardDescription>
{props.achievement.description}
</ListCardDescription>
<ListCardBody>
Id: <b>{props.achievement.id}</b> <br />
Required Points/Event Completions:{" "}
<b>{props.achievement.requiredPoints}</b> <br />
Linked Event ID: <b>{props.achievement.eventId ?? "NONE"}</b> <br />
Location Type: <b>{props.achievement.locationType}</b> <br />
Achievement Type: <b>{props.achievement.achievementType}</b> <br />
</ListCardBody>
<ListCardButtons>
<HButton onClick={props.onDelete}>DELETE</HButton>
<HButton onClick={props.onEdit} float="right">
EDIT
</HButton>
</ListCardButtons>
</ListCardBox>
</>
);
}

// Default Form Creation
function makeForm() {
return [
{ name: "Name", characterLimit: 256, value: "" },
{ name: "Description", characterLimit: 2048, value: "" },
{
name: "Location Type",
options: locationOptions as string[],
value: 0,
},
{
name: "Achievement Type",
options: achievementOptions as string[],
value: 0,
},
{ name: "Required Points", value: 1, min: 1, max: 999 },
] as EntryForm[];
}

// Form to DTO Conversion
function fromForm(form: EntryForm[], id: string): AchievementDto {
return {
id,
imageUrl: "",
name: (form[0] as FreeEntryForm).value,
description: (form[1] as FreeEntryForm).value,
locationType: locationOptions[(form[2] as OptionEntryForm).value],
achievementType: achievementOptions[(form[3] as OptionEntryForm).value],
requiredPoints: (form[4] as NumberEntryForm).value,
};
}

// DTO to Form Conversion
function toForm(achievement: AchievementDto) {
return [
{ name: "Name", characterLimit: 256, value: achievement.name! },
{
name: "Description",
characterLimit: 2048,
value: achievement.description!,
},
{
name: "Location Type",
options: locationOptions as string[],
value: locationOptions.indexOf(achievement.locationType!),
},
{
name: "Achievement Type",
options: achievementOptions as string[],
value: achievementOptions.indexOf(achievement.achievementType!),
},
{
name: "Required Points",
value: achievement.requiredPoints!,
min: 1,
max: 999,
},
] as EntryForm[];
}

export function Achievements() {
const serverData = useContext(ServerDataContext);
const [isCreateModalOpen, setCreateModalOpen] = useState(false);
const [isEditModalOpen, setEditModalOpen] = useState(false);
const [isDeleteModalOpen, setDeleteModalOpen] = useState(false);
const [selectModalOpen, setSelectModalOpen] = useState(false);
const [isLinkedModalOpen, setLinkedModalOpen] = useState(false);
const [form, setForm] = useState(() => makeForm());
const [currentId, setCurrentId] = useState("");
const [query, setQuery] = useState("");
const selectedOrg = serverData.organizations.get(serverData.selectedOrg);

return (
<>
<AlertModal
description="To create an achievement, select an organization."
isOpen={selectModalOpen}
onClose={() => setSelectModalOpen(false)}
/>
<AlertModal
description="To link an achievement to an event, select an event."
isOpen={isLinkedModalOpen}
onClose={() => setLinkedModalOpen(false)}
/>
<EntryModal
title="Create Achievement"
isOpen={isCreateModalOpen}
entryButtonText="CREATE"
onEntry={() => {
serverData.updateAchievement({
...fromForm(form, ""),
initialOrganizationId: serverData.selectedOrg,
});
setCreateModalOpen(false);
}}
onCancel={() => {
setCreateModalOpen(false);
}}
form={form}
/>
<EntryModal
title="Edit Event"
isOpen={isEditModalOpen}
entryButtonText="EDIT"
onEntry={() => {
const oldAchievement = serverData.achievements.get(currentId)!;
serverData.updateAchievement({
...oldAchievement,
...fromForm(form, currentId),
});
setEditModalOpen(false);
}}
onCancel={() => {
setEditModalOpen(false);
}}
form={form}
/>
<DeleteModal
objectName={serverData.achievements.get(currentId)?.name ?? ""}
isOpen={isDeleteModalOpen}
onClose={() => setDeleteModalOpen(false)}
onDelete={() => {
serverData.deleteAchievement(currentId);
setDeleteModalOpen(false);
}}
/>
<SearchBar
onCreate={() => {
if (!selectedOrg) {
setSelectModalOpen(true);
return;
}
setForm(makeForm());
setCreateModalOpen(true);
}}
onSearch={(query) => setQuery(query)}
/>
{serverData.selectedOrg === "" ? (
<CenterText>Select an organization to view achievements</CenterText>
) : serverData.organizations.get(serverData.selectedOrg) ? (
serverData.organizations?.get(serverData.selectedOrg)?.achivements
?.length === 0 && (
<CenterText>No achievements in organization</CenterText>
)
) : (
<CenterText>Error getting achievements</CenterText>
)}
{Array.from<AchievementDto>(
serverData.organizations
.get(serverData.selectedOrg)
?.achivements?.map(
(achId: string) => serverData.achievements.get(achId)!
)
.filter((ach?: AchievementDto) => !!ach) ?? []
)
.sort(
(a: AchievementDto, b: AchievementDto) =>
compareTwoStrings(b.name ?? "", query) -
compareTwoStrings(a.name ?? "", query) +
compareTwoStrings(b.description ?? "", query) -
compareTwoStrings(a.description ?? "", query)
)
.map((ach) => (
<AchiemementCard
key={ach.id}
achievement={ach}
onSelect={() => {
if (ach.eventId) {
serverData.updateAchievement({
...ach,
eventId: "",
});

return;
}

if (serverData.selectedEvent === "") {
setLinkedModalOpen(true);
} else {
serverData.updateAchievement({
...ach,
eventId: serverData.selectedEvent,
});
}
}}
onDelete={() => {
setCurrentId(ach.id);
setDeleteModalOpen(true);
}}
onEdit={() => {
setCurrentId(ach.id);
setForm(toForm(ach));
setEditModalOpen(true);
}}
/>
))}
</>
);
}
4 changes: 4 additions & 0 deletions admin/src/components/ServerApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export class ServerApi {
this.send("requestAchievementData", data);
}

requestAchievementTrackerData(data: dto.RequestAchievementTrackerDataDto) {
this.send("requestAchievementTrackerData", data);
}

updateAchievementData(data: dto.UpdateAchievementDataDto) {
this.send("updateAchievementData", data);
}
Expand Down
Loading

0 comments on commit 1d3fdd8

Please sign in to comment.