Tutorial Working
This commit is contained in:
17
src/components/TutorialComponent.tsx
Normal file
17
src/components/TutorialComponent.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { CallBackProps, Joyride, Props } from "react-joyride";
|
||||
|
||||
|
||||
interface TutorialComponentProps extends Props {
|
||||
updateFunction: () => void;
|
||||
}
|
||||
|
||||
export default function TutorialComponent(props: TutorialComponentProps){
|
||||
return Joyride({
|
||||
...props,
|
||||
callback: (callbackProps: CallBackProps) => {
|
||||
if(callbackProps.type === "tour:end"){
|
||||
props.updateFunction();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -33,8 +33,6 @@ export default function ProtectedNavLinks(){
|
||||
path: "/logout"
|
||||
});
|
||||
|
||||
console.log("is admin = " + isSiteAdmin(accountPermissions));
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { HTMLProps, useState } from "react";
|
||||
|
||||
|
||||
export interface Tab {
|
||||
tabId?: string;
|
||||
tabHeader: React.ReactNode;
|
||||
headerClasses?: string;
|
||||
tabContent: React.ReactNode;
|
||||
@@ -39,6 +40,7 @@ export default function TabGroup(props: TabGroupProps){
|
||||
{
|
||||
tabs.map((tab, index) => (
|
||||
<TabHeader
|
||||
id={tab.tabId}
|
||||
key={index}
|
||||
tab={tab}
|
||||
active={activeTab === index}
|
||||
@@ -71,16 +73,19 @@ export default function TabGroup(props: TabGroupProps){
|
||||
|
||||
|
||||
function TabHeader({
|
||||
id,
|
||||
tab,
|
||||
onClick,
|
||||
active
|
||||
}:{
|
||||
id?: string;
|
||||
tab: Tab;
|
||||
onClick: () => void;
|
||||
active: boolean;
|
||||
}){
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className={clsx(
|
||||
tab.headerClasses,
|
||||
"px-4 py-2 rounded-t-lg cursor-pointer whitespace-nowrap",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Account } from "@/interface/Account";
|
||||
import { AccountTutorialStatus } from "@/interface/AccountTutorialStatus";
|
||||
import { RaidGroupPermissionType } from "@/interface/RaidGroup";
|
||||
import { api } from "@/util/AxiosUtil";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
@@ -124,6 +125,41 @@ export function useGetAccountsByRaidGroupCount(raidGroupId: string, searchTerm?:
|
||||
});
|
||||
}
|
||||
|
||||
export function useGetTutorialsStatus(accountId: string | null){
|
||||
return useQuery({
|
||||
queryKey: ["tutorials", "account", accountId],
|
||||
queryFn: async () => {
|
||||
const response = await api.get(`/account/tutorial`);
|
||||
|
||||
if(response.status !== 200){
|
||||
throw new Error("Failed to get tutorials status");
|
||||
}
|
||||
else if(response.data.errors){
|
||||
throw new Error(response.data.errors.join(", "));
|
||||
}
|
||||
|
||||
return response.data as AccountTutorialStatus;
|
||||
},
|
||||
enabled: !!accountId
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateTutorialsStatus(){
|
||||
return useMutation({
|
||||
mutationKey: ["tutorials", "accounts"],
|
||||
mutationFn: async (tutorials: AccountTutorialStatus) => {
|
||||
const response = await api.put(`/account/tutorial`, tutorials);
|
||||
|
||||
if(response.status !== 200){
|
||||
throw new Error("Failed to update tutorials status");
|
||||
}
|
||||
else if(response.data.errors){
|
||||
throw new Error(response.data.errors.join(", "));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function useForcePasswordReset(accountId: string){
|
||||
|
||||
15
src/interface/AccountTutorialStatus.ts
Normal file
15
src/interface/AccountTutorialStatus.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export enum TutorialStatus {
|
||||
COMPLETED = "COMPLETED",
|
||||
NOT_COMPLETED = "NOT_COMPLETED"
|
||||
}
|
||||
|
||||
|
||||
export interface AccountTutorialStatus {
|
||||
accountTutorialStatusId?: string;
|
||||
accountId: string;
|
||||
gamesTutorialStatus: TutorialStatus;
|
||||
gameTutorialStatus: TutorialStatus;
|
||||
raidGroupsTutorialStatus: TutorialStatus;
|
||||
raidGroupTutorialStatus: TutorialStatus;
|
||||
instanceTutorialStatus: TutorialStatus;
|
||||
}
|
||||
@@ -1,33 +1,39 @@
|
||||
import DangerMessage from "@/components/message/DangerMessage";
|
||||
import TabGroup, { Tab } from "@/components/tab/TabGroup";
|
||||
import TutorialComponent from "@/components/TutorialComponent";
|
||||
import { useGetGame } from "@/hooks/GameHooks";
|
||||
import { TutorialStatus } from "@/interface/AccountTutorialStatus";
|
||||
import { Game } from "@/interface/Game";
|
||||
import { useAuth } from "@/providers/AuthProvider";
|
||||
import GameCalendarDisplay from "@/ui/calendar/GameCalendarDisplay";
|
||||
import GameHeader from "@/ui/game/GameHeader";
|
||||
import GameClassDisplay from "@/ui/gameClass/GameClassDisplay";
|
||||
import RaidGroupsByGameDisplay from "@/ui/raidGroup/RaidGroupsByGameDisplay";
|
||||
import { gameTutorialSteps } from "@/util/TutorialUtil";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Navigate, useParams } from "react-router";
|
||||
|
||||
|
||||
export default function GamePage(){
|
||||
const { gameId } = useParams();
|
||||
const tabs: Tab[] = [
|
||||
{
|
||||
tabHeader: "Calendar",
|
||||
tabContent: <GameCalendarDisplay gameId={gameId!}/>
|
||||
},
|
||||
{
|
||||
tabHeader: "Raid Groups",
|
||||
tabContent: <RaidGroupsByGameDisplay gameId={gameId!}/>
|
||||
},
|
||||
{
|
||||
tabHeader: "Classes",
|
||||
tabContent: <GameClassDisplay gameId={gameId!}/>
|
||||
}
|
||||
];
|
||||
|
||||
const [ game, setGame ] = useState<Game>();
|
||||
const { tutorialsStatus, setTutorialsStatus } = useAuth();
|
||||
|
||||
const gameQuery = useGetGame(gameId ?? "", false);
|
||||
|
||||
|
||||
const updateGameTutorialStatus = () => {
|
||||
const newTutorialsStatus = { ...tutorialsStatus };
|
||||
if(tutorialsStatus.gameTutorialStatus === TutorialStatus.COMPLETED){
|
||||
newTutorialsStatus.gameTutorialStatus = TutorialStatus.NOT_COMPLETED;
|
||||
}
|
||||
else if(tutorialsStatus.gameTutorialStatus === TutorialStatus.NOT_COMPLETED){
|
||||
newTutorialsStatus.gameTutorialStatus = TutorialStatus.COMPLETED;
|
||||
}
|
||||
setTutorialsStatus(newTutorialsStatus);
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if(gameQuery.status === "success"){
|
||||
setGame(gameQuery.data);
|
||||
@@ -42,7 +48,7 @@ export default function GamePage(){
|
||||
}
|
||||
else if(gameQuery.status === "error"){
|
||||
return (
|
||||
<div>Error</div>
|
||||
<DangerMessage>Error: {gameQuery.error.message}</DangerMessage>
|
||||
);
|
||||
}
|
||||
else if(gameQuery.status === "success" && gameQuery.data === undefined){
|
||||
@@ -51,10 +57,39 @@ export default function GamePage(){
|
||||
);
|
||||
}
|
||||
else if(game){
|
||||
const tabs: Tab[] = [
|
||||
{
|
||||
tabId: "calendarTab",
|
||||
tabHeader: "Calendar",
|
||||
tabContent: <GameCalendarDisplay gameId={gameId!}/>
|
||||
},
|
||||
{
|
||||
tabId: "raidGroupsTab",
|
||||
tabHeader: "Raid Groups",
|
||||
tabContent: <RaidGroupsByGameDisplay gameId={gameId!}/>
|
||||
},
|
||||
{
|
||||
tabId: "gameClassesTab",
|
||||
tabHeader: "Classes",
|
||||
tabContent: <GameClassDisplay gameId={gameId!}/>
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
return (
|
||||
<main
|
||||
className="flex flex-col items-center justify-center"
|
||||
>
|
||||
{/* Tutorials */}
|
||||
<TutorialComponent
|
||||
steps={gameTutorialSteps}
|
||||
run={tutorialsStatus.gameTutorialStatus === TutorialStatus.NOT_COMPLETED}
|
||||
showProgress={true}
|
||||
showSkipButton={true}
|
||||
continuous={true}
|
||||
disableScrolling={true}
|
||||
updateFunction={updateGameTutorialStatus}
|
||||
/>
|
||||
<GameHeader
|
||||
game={game}
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,35 @@
|
||||
import TutorialComponent from "@/components/TutorialComponent";
|
||||
import { TutorialStatus } from "@/interface/AccountTutorialStatus";
|
||||
import { useAuth } from "@/providers/AuthProvider";
|
||||
import AllGamesDisplay from "@/ui/game/AllGamesDisplay";
|
||||
import { gamesTutorialSteps } from "@/util/TutorialUtil";
|
||||
|
||||
|
||||
export default function GamesPage(){
|
||||
const { tutorialsStatus, setTutorialsStatus } = useAuth();
|
||||
|
||||
|
||||
const updateGamesTutorialStatus = () => {
|
||||
const newTutorialsStatus = { ...tutorialsStatus };
|
||||
if(tutorialsStatus.gamesTutorialStatus === TutorialStatus.COMPLETED){
|
||||
newTutorialsStatus.gamesTutorialStatus = TutorialStatus.NOT_COMPLETED;
|
||||
}
|
||||
else if(tutorialsStatus.gamesTutorialStatus === TutorialStatus.NOT_COMPLETED){
|
||||
newTutorialsStatus.gamesTutorialStatus = TutorialStatus.COMPLETED;
|
||||
}
|
||||
setTutorialsStatus(newTutorialsStatus);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<main
|
||||
className="flex flex-col items-center justify-center gap-8 w-full"
|
||||
className="flex flex-col items-center justify-center gap-y-8 w-full"
|
||||
>
|
||||
<TutorialComponent
|
||||
steps={gamesTutorialSteps}
|
||||
run={tutorialsStatus.gamesTutorialStatus === TutorialStatus.NOT_COMPLETED}
|
||||
updateFunction={updateGamesTutorialStatus}
|
||||
/>
|
||||
<h1
|
||||
className="text-4xl"
|
||||
>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import DangerMessage from "@/components/message/DangerMessage";
|
||||
import TabGroup, { Tab } from "@/components/tab/TabGroup";
|
||||
import TutorialComponent from "@/components/TutorialComponent";
|
||||
import { useGetRaidGroup } from "@/hooks/RaidGroupHooks";
|
||||
import { TutorialStatus } from "@/interface/AccountTutorialStatus";
|
||||
import { RaidGroup } from "@/interface/RaidGroup";
|
||||
import { useAuth } from "@/providers/AuthProvider";
|
||||
import RaidGroupAccountsTab from "@/ui/account/RaidGroupAccountsTab";
|
||||
@@ -12,17 +14,20 @@ import RaidGroupRequestTab from "@/ui/raidGroupRequest/RaidGroupRequestTab";
|
||||
import RaidInstanceTab from "@/ui/raidInstance/RaidInstanceTab";
|
||||
import RaidLayoutTab from "@/ui/raidLayout/RaidLayoutTab";
|
||||
import { isRaidGroupAdmin } from "@/util/PermissionUtil";
|
||||
import { raidGroupTutorialSteps } from "@/util/TutorialUtil";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Navigate, useParams } from "react-router";
|
||||
|
||||
|
||||
export default function RaidGroupPage(){
|
||||
const { raidGroupId } = useParams();
|
||||
const { accountPermissions, raidGroupPermissions } = useAuth();
|
||||
|
||||
const { accountPermissions, raidGroupPermissions, tutorialsStatus, setTutorialsStatus } = useAuth();
|
||||
|
||||
const [ raidGroup, setRaidGroup ] = useState<RaidGroup>();
|
||||
|
||||
|
||||
const raidGroupQuery = useGetRaidGroup(raidGroupId ?? "", false);
|
||||
|
||||
useEffect(() => {
|
||||
if(raidGroupQuery.status === "success"){
|
||||
setRaidGroup(raidGroupQuery.data);
|
||||
@@ -30,6 +35,18 @@ export default function RaidGroupPage(){
|
||||
}, [ raidGroupQuery ]);
|
||||
|
||||
|
||||
const updateRaidGroupTutorialStatus = () => {
|
||||
const newTutorialsStatus = { ...tutorialsStatus };
|
||||
if(tutorialsStatus.raidGroupTutorialStatus === TutorialStatus.COMPLETED){
|
||||
newTutorialsStatus.raidGroupTutorialStatus = TutorialStatus.NOT_COMPLETED;
|
||||
}
|
||||
else if(tutorialsStatus.raidGroupTutorialStatus === TutorialStatus.NOT_COMPLETED){
|
||||
newTutorialsStatus.raidGroupTutorialStatus = TutorialStatus.COMPLETED;
|
||||
}
|
||||
setTutorialsStatus(newTutorialsStatus);
|
||||
}
|
||||
|
||||
|
||||
if(raidGroupQuery.status === "pending"){
|
||||
return (
|
||||
<div>Loading...</div>
|
||||
@@ -48,22 +65,27 @@ export default function RaidGroupPage(){
|
||||
else if(raidGroup){
|
||||
const tabs: Tab[] = [
|
||||
{
|
||||
tabId: "calendarTab",
|
||||
tabHeader: "Calendar",
|
||||
tabContent: <RaidGroupCalendarDisplay gameId={raidGroup?.gameId ?? ""} raidGroupId={raidGroupId!}/>
|
||||
},
|
||||
{
|
||||
tabId: "peopleTab",
|
||||
tabHeader: "People",
|
||||
tabContent: <PersonTab raidGroup={raidGroup}/>
|
||||
},
|
||||
{
|
||||
tabId: "classGroupsTab",
|
||||
tabHeader: "Class Groups",
|
||||
tabContent: <ClassGroupsTab raidGroup={raidGroup}/>
|
||||
},
|
||||
{
|
||||
tabId: "raidLayoutsTab",
|
||||
tabHeader: "Raid Layout",
|
||||
tabContent: <RaidLayoutTab raidGroup={raidGroup}/>
|
||||
},
|
||||
{
|
||||
tabId: "instancesTab",
|
||||
tabHeader: "Raid Instances",
|
||||
tabContent: <RaidInstanceTab raidGroup={raidGroup}/>
|
||||
}
|
||||
@@ -71,10 +93,12 @@ export default function RaidGroupPage(){
|
||||
|
||||
if(isRaidGroupAdmin(raidGroupId!, raidGroupPermissions, accountPermissions)){
|
||||
tabs.push({
|
||||
tabId: "usersTab",
|
||||
tabHeader: "Users",
|
||||
tabContent: <RaidGroupAccountsTab raidGroup={raidGroup}/>
|
||||
});
|
||||
tabs.push({
|
||||
tabId: "requestsTab",
|
||||
tabHeader: "Requests",
|
||||
tabContent: <RaidGroupRequestTab raidGroup={raidGroup}/>
|
||||
});
|
||||
@@ -85,6 +109,15 @@ export default function RaidGroupPage(){
|
||||
<main
|
||||
className="flex flex-col items-center justify-center gap-y-8"
|
||||
>
|
||||
<TutorialComponent
|
||||
steps={raidGroupTutorialSteps}
|
||||
run={tutorialsStatus.raidGroupTutorialStatus === TutorialStatus.NOT_COMPLETED}
|
||||
showProgress={true}
|
||||
showSkipButton={true}
|
||||
continuous={true}
|
||||
disableScrolling={true}
|
||||
updateFunction={updateRaidGroupTutorialStatus}
|
||||
/>
|
||||
<RaidGroupHeader
|
||||
raidGroup={raidGroup}
|
||||
/>
|
||||
|
||||
@@ -1,15 +1,35 @@
|
||||
import TutorialComponent from "@/components/TutorialComponent";
|
||||
import { TutorialStatus } from "@/interface/AccountTutorialStatus";
|
||||
import { useAuth } from "@/providers/AuthProvider";
|
||||
import RaidGroupsByAccountDisplay from "@/ui/raidGroup/RaidGroupsByAccountDisplay";
|
||||
import { raidGroupsTutorialSteps } from "@/util/TutorialUtil";
|
||||
|
||||
|
||||
export default function RaidGroupsPage(){
|
||||
const { accountId } = useAuth();
|
||||
const { accountId, tutorialsStatus, setTutorialsStatus } = useAuth();
|
||||
|
||||
|
||||
const updateRaidGroupsTutorialStatus = () => {
|
||||
const newTutorialsStatus = { ...tutorialsStatus };
|
||||
if(tutorialsStatus.raidGroupsTutorialStatus === TutorialStatus.COMPLETED){
|
||||
newTutorialsStatus.raidGroupsTutorialStatus = TutorialStatus.NOT_COMPLETED;
|
||||
}
|
||||
else if(tutorialsStatus.raidGroupsTutorialStatus === TutorialStatus.NOT_COMPLETED){
|
||||
newTutorialsStatus.raidGroupsTutorialStatus = TutorialStatus.COMPLETED;
|
||||
}
|
||||
setTutorialsStatus(newTutorialsStatus);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<main
|
||||
className="flex flex-col items-center justify-center gap-8"
|
||||
>
|
||||
<TutorialComponent
|
||||
steps={raidGroupsTutorialSteps}
|
||||
run={tutorialsStatus.raidGroupsTutorialStatus === TutorialStatus.NOT_COMPLETED}
|
||||
updateFunction={updateRaidGroupsTutorialStatus}
|
||||
/>
|
||||
<h1
|
||||
className="text-4xl"
|
||||
>
|
||||
|
||||
@@ -1,15 +1,32 @@
|
||||
import PrimaryButton from "@/components/button/PrimaryButton";
|
||||
import TutorialComponent from "@/components/TutorialComponent";
|
||||
import { TutorialStatus } from "@/interface/AccountTutorialStatus";
|
||||
import { useAuth } from "@/providers/AuthProvider";
|
||||
import RaidInstanceLayoutProvider from "@/providers/RaidInstanceLayoutProvider";
|
||||
import RaidInstanceCreatorUI from "@/ui/raidInstance/creator/RaidInstanceCreatorUI";
|
||||
import { instanceTutorialSteps } from "@/util/TutorialUtil";
|
||||
import { BsArrowLeft } from "react-icons/bs";
|
||||
import { useNavigate, useParams } from "react-router";
|
||||
|
||||
|
||||
export default function RaidInstancePage(){
|
||||
const { raidGroupId, raidInstanceId } = useParams();
|
||||
const { tutorialsStatus, setTutorialsStatus } = useAuth();
|
||||
const navigator = useNavigate();
|
||||
|
||||
|
||||
const updateGamesTutorialStatus = () => {
|
||||
const newTutorialsStatus = { ...tutorialsStatus };
|
||||
if(tutorialsStatus.instanceTutorialStatus === TutorialStatus.COMPLETED){
|
||||
newTutorialsStatus.instanceTutorialStatus = TutorialStatus.NOT_COMPLETED;
|
||||
}
|
||||
else if(tutorialsStatus.instanceTutorialStatus === TutorialStatus.NOT_COMPLETED){
|
||||
newTutorialsStatus.instanceTutorialStatus = TutorialStatus.COMPLETED;
|
||||
}
|
||||
setTutorialsStatus(newTutorialsStatus);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<RaidInstanceLayoutProvider
|
||||
raidGroupId={raidGroupId ?? ""}
|
||||
@@ -18,6 +35,15 @@ export default function RaidInstancePage(){
|
||||
<main
|
||||
className="flex flex-col items-center justify-center"
|
||||
>
|
||||
<TutorialComponent
|
||||
steps={instanceTutorialSteps}
|
||||
run={tutorialsStatus.instanceTutorialStatus === TutorialStatus.NOT_COMPLETED}
|
||||
showProgress={true}
|
||||
showSkipButton={true}
|
||||
continuous={true}
|
||||
disableScrolling={true}
|
||||
updateFunction={updateGamesTutorialStatus}
|
||||
/>
|
||||
{/* Back to Raid Group Button */}
|
||||
<div
|
||||
className="flex flex-row items-center justify-start w-full my-8"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { useGetTutorialsStatus, useUpdateTutorialsStatus } from "@/hooks/AccountHooks";
|
||||
import { AccountPermission } from "@/interface/AccountPermission";
|
||||
import { AccountTutorialStatus } from "@/interface/AccountTutorialStatus";
|
||||
import { GamePermission } from "@/interface/GamePermission";
|
||||
import { RaidGroupPermission } from "@/interface/RaidGroupPermission";
|
||||
import { RaidGroupRequest } from "@/interface/RaidGroupRequest";
|
||||
@@ -23,6 +25,8 @@ type AuthProviderState = {
|
||||
raidGroupPermissions: RaidGroupPermission[];
|
||||
gamePermissions: GamePermission[];
|
||||
raidGroupRequests: RaidGroupRequest[];
|
||||
tutorialsStatus: AccountTutorialStatus;
|
||||
setTutorialsStatus: (tutorialsStatus: AccountTutorialStatus) => void;
|
||||
}
|
||||
|
||||
const initialState: AuthProviderState = {
|
||||
@@ -34,7 +38,9 @@ const initialState: AuthProviderState = {
|
||||
accountPermissions: [],
|
||||
raidGroupPermissions: [],
|
||||
gamePermissions: [],
|
||||
raidGroupRequests: []
|
||||
raidGroupRequests: [],
|
||||
tutorialsStatus: {} as AccountTutorialStatus,
|
||||
setTutorialsStatus: () => null
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthProviderState>(initialState);
|
||||
@@ -51,6 +57,11 @@ export function AuthProvider({
|
||||
const [ raidGroupPermissions, setRaidGroupPermissions ] = useState<RaidGroupPermission[]>([]);
|
||||
const [ gamePermissions, setGamePermissions ] = useState<GamePermission[]>([]);
|
||||
const [ raidGroupRequests, setRaidGroupRequests ] = useState<RaidGroupRequest[]>([]);
|
||||
const [ tutorialsStatus, setTutorialsStatus ] = useState<AccountTutorialStatus>({} as AccountTutorialStatus);
|
||||
|
||||
|
||||
const tutorialsStatusQuery = useGetTutorialsStatus(accountId);
|
||||
const { mutate: tutorialsStatusMutation } = useUpdateTutorialsStatus();
|
||||
|
||||
|
||||
const fetchToken = useCallback(async () => {
|
||||
@@ -112,18 +123,29 @@ export function AuthProvider({
|
||||
fetchToken();
|
||||
}, [ fetchToken ]);
|
||||
|
||||
//Update the tutorial status when fetched
|
||||
useEffect(() => {
|
||||
if(tutorialsStatusQuery.status === "success"){
|
||||
setTutorialsStatus(tutorialsStatusQuery.data);
|
||||
}
|
||||
}, [ tutorialsStatusQuery.status, tutorialsStatusQuery.data ]);
|
||||
|
||||
const updateTutorialsStatus = useCallback((newTutorialsStatus: AccountTutorialStatus) => {
|
||||
setTutorialsStatus(newTutorialsStatus);
|
||||
tutorialsStatusMutation(newTutorialsStatus);
|
||||
}, [ setTutorialsStatus, tutorialsStatusMutation ]);
|
||||
|
||||
|
||||
const currentTokens = useMemo(() => ({
|
||||
jwt,
|
||||
setJwt,
|
||||
expiration,
|
||||
setExpiration,
|
||||
jwt, setJwt,
|
||||
expiration, setExpiration,
|
||||
accountId,
|
||||
accountPermissions,
|
||||
raidGroupPermissions,
|
||||
gamePermissions,
|
||||
raidGroupRequests
|
||||
}), [ jwt, setJwt, expiration, setExpiration, accountId, accountPermissions, raidGroupPermissions, gamePermissions, raidGroupRequests ]);
|
||||
raidGroupRequests,
|
||||
tutorialsStatus, setTutorialsStatus: updateTutorialsStatus
|
||||
}), [ jwt, expiration, accountId, accountPermissions, raidGroupPermissions, gamePermissions, raidGroupRequests, tutorialsStatus, updateTutorialsStatus ]);
|
||||
|
||||
|
||||
//TODO: Return a spinner while the first token is being fetched
|
||||
|
||||
@@ -195,6 +195,7 @@ export default function RaidInstanceHeader(){
|
||||
>
|
||||
{
|
||||
<PrimaryButton
|
||||
id="saveInstanceButton"
|
||||
onClick={saveRaidInstance}
|
||||
>
|
||||
Save
|
||||
|
||||
@@ -42,6 +42,7 @@ export default function RaidInstanceCreatorTable({
|
||||
</div>
|
||||
{/* Buttons */}
|
||||
<PrimaryButton
|
||||
id="instanceAddRunButton"
|
||||
onClick={addRun}
|
||||
>
|
||||
Add Run
|
||||
|
||||
@@ -104,6 +104,7 @@ export default function RaidInstanceCreatorTableBody({
|
||||
className="flex flex-row justify-center items-center cursor-pointer p-2 space-x-2"
|
||||
>
|
||||
<DangerButton
|
||||
id={`removeRun${runIndex}`}
|
||||
variant="ghost"
|
||||
shape="square"
|
||||
onClick={() => removeRun(runIndex)}
|
||||
@@ -114,6 +115,7 @@ export default function RaidInstanceCreatorTableBody({
|
||||
/>
|
||||
</DangerButton>
|
||||
<TertiaryButton
|
||||
id={`copyDiscordString${runIndex}`}
|
||||
variant="ghost"
|
||||
shape="square"
|
||||
onClick={() => copyDiscordStringToClipBoard(runIndex)}
|
||||
|
||||
253
src/util/TutorialUtil.ts
Normal file
253
src/util/TutorialUtil.ts
Normal file
@@ -0,0 +1,253 @@
|
||||
import { Step } from "react-joyride";
|
||||
|
||||
|
||||
const nextButtonStyles = {
|
||||
backgroundColor: "#0000FF"
|
||||
};
|
||||
|
||||
const backButtonStyles = {
|
||||
color: "#0000FF"
|
||||
}
|
||||
|
||||
|
||||
export const gamesTutorialSteps: Array<Step> = [
|
||||
{
|
||||
target: "body",
|
||||
content: "This is the games page. It allows you to select a game to view its details and Raid Groups.",
|
||||
placement: "center",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
export const gameTutorialSteps: Array<Step> = [
|
||||
{
|
||||
target: "body",
|
||||
content: "This is the games page. From here you can see info about the game and groups that play this game.",
|
||||
placement: "center",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles,
|
||||
buttonBack: backButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#calendarTab",
|
||||
content: "This is the Calendar tab. It is populated with game-wide events by Game Admins.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles,
|
||||
buttonBack: backButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#raidGroupsTab",
|
||||
content: "This tab displays all the Raid Groups associated with this game. " +
|
||||
"If you already belong to a Raid Group you can go to tis Raid Group page. " +
|
||||
"You can also request to join an existing Raid Group or create your own.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles,
|
||||
buttonBack: backButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#gameClassesTab",
|
||||
content: "This tab displays all the classes in the game. " +
|
||||
"From here you can update, add, or delete information related to the classes.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles,
|
||||
buttonBack: backButtonStyles
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export const raidGroupsTutorialSteps: Array<Step> = [
|
||||
{
|
||||
target: "body",
|
||||
content: "This is the Raid Groups page. " +
|
||||
"This is where you can see all of the Raid Groups to which you belong. " +
|
||||
"When you select one of the Group you will be taken to their Raid Group page.",
|
||||
placement: "center",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export const raidGroupTutorialSteps: Array<Step> = [
|
||||
{
|
||||
target: "body",
|
||||
content: "This is the Raid Group page. " +
|
||||
"From here you can see everything that is happening in the Raid Group.",
|
||||
placement: "center",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#peopleTab",
|
||||
content: "Start your Raid Group by adding the people who will be participating in your raids. " +
|
||||
"By adding the person's Discord ID you will be able to copy raids directly from Raid Builder into Discord with the user tagged.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#peopleTab",
|
||||
content: "Once a person has been added you can add characters to that person. " +
|
||||
"These characters can then be used to make a roster. " +
|
||||
"In order to edit or delete a character you can click the person's name to go to the details page.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "body",
|
||||
content: "This is all that is required to begin creating rosters. " +
|
||||
"However, there are a few more steps you can take to make creating rosters faster and easier.",
|
||||
placement: "center",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#classGroupsTab",
|
||||
content: "Class Groups allow you to define roles in a raid and determine which classes are capable of filling that role.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#classGroupsTab",
|
||||
content: "As an example:\n" +
|
||||
"You could create a Class Group named \"Damage\" and include the classes \"Hunter\" and \"Rogue\"." +
|
||||
"Then, during roster creation you can select the \"Damage\" role as required. " +
|
||||
"This will automatically filter the characters so you only see Hunters and Rogues when filling that spot in the roster.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#raidLayoutsTab",
|
||||
content: "Raid Layouts allow you to save the makeup of a raid, in regards to which Class Groups are required. " +
|
||||
"Whether this is a raid you run often, or the general makeup of a good group, " +
|
||||
"having a Layout before you create a roster will automatically fill in the Class Groups section of the roster, " +
|
||||
"meaning all characters are correctly filtered for each role from the start.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#instancesTab",
|
||||
content: "This tab holds all of the rosters that you have created. " +
|
||||
"They can have distinct names and times, and you can even create multiple rosters within the same instance to facilitate instance farming.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#calendarTab",
|
||||
content: "The Calendar tab is where you can see all of the events that are happening in the Raid Group. " +
|
||||
"These events include the events from the Game, as well as Instances and any other custom events that Raid Group admins have created.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#usersTab",
|
||||
content: "The users tab displays website accounts that have access to the Raid Group and let you control the permission level of each user.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#requestsTab",
|
||||
content: "The Requests tab shows users that have requested access to your Raid Group. " +
|
||||
"Once you have reviewed the request you can deny it, or accept it and choose the user's permission level.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
export const instanceTutorialSteps: Array<Step> = [
|
||||
{
|
||||
target: "body",
|
||||
content: "This is the Raid Instance page. " +
|
||||
"This is where you can setup rosters for a run.",
|
||||
placement: "center",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#selectRaidLayoutButton",
|
||||
content: "To start you can pick an existing Raid Layout, which will automatically filter characters in each slot for you.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#instanceTableHead",
|
||||
content: "You can also create an adhoc raid by clicking one of the Any cells and selecting Class Groups directly.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#instanceTableBody",
|
||||
content: "Or you can begin by clicking None and selecting characters to fill each slot.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#saveInstanceButton",
|
||||
content: "You can save the Instance at any time. This will cause it to appear on the Instance and Calendar tabs. " +
|
||||
"You can also come back and edit it later if needed.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#instanceAddRunButton",
|
||||
content: "Click this button to add another run to the instance.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#removeRun0",
|
||||
content: "Click this button to remove this run from the instance.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "#copyDiscordString0",
|
||||
content: "Click this button to copy a string to your clipboard that is ready to be pasted into Discord.",
|
||||
placement: "bottom",
|
||||
styles: {
|
||||
buttonNext: nextButtonStyles
|
||||
}
|
||||
}
|
||||
];
|
||||
Reference in New Issue
Block a user