Skip to content

Commit

Permalink
feat: system announcement improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
jakeaturner committed Jul 12, 2024
1 parent f5b5415 commit 0b5f5cb
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 88 deletions.
34 changes: 5 additions & 29 deletions client/src/Commons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import "./components/commons/Commons.css";
import { useTypedSelector } from "./state/hooks";
import { Announcement } from "./types";
import LoadingSpinner from "./components/LoadingSpinner";
import useSystemAnnouncement from "./hooks/useSystemAnnouncement";

/**
* The public-facing catalog and showcase application.
Expand All @@ -29,22 +30,12 @@ const Commons = () => {
const location = useLocation();
const org = useTypedSelector((state) => state.org);
const user = useTypedSelector((state) => state.user);
const { sysAnnouncement } = useSystemAnnouncement();
const LAUNCHPAD_URL = "https://one.libretexts.org/launchpad";

// Menu state
const [activeItem, setActiveItem] = useState("");

// System Announcement
const [systemAnnouncement, setSystemAnnouncement] =
useState<Announcement | null>(null);

/**
* Check for any available global System Announcements on load.
*/
useEffect(() => {
loadSystemAnnouncements();
}, []);

/**
* Subscribe to changes to location and update the Menu with the active page.
*/
Expand All @@ -63,30 +54,15 @@ const Commons = () => {
}
}, [location.pathname]);

async function loadSystemAnnouncements() {
try {
const res = await axios.get("/announcements/system");
if (res.data.err) {
throw new Error(res.data.errMsg);
}

if (res.data.sysAnnouncement !== null) {
setSystemAnnouncement(res.data.sysAnnouncement);
}
} catch (err) {
console.error(err); // fail silently
}
}

return (
<div className="commons">
<CommonsNavbar org={org} user={user} />
<CommonsJumbotron backgroundURL={org.coverPhoto ?? ""} />
<CommonsMenu activeItem={activeItem} />
{systemAnnouncement && (
{sysAnnouncement && (
<SystemAnnouncement
title={systemAnnouncement.title}
message={systemAnnouncement.message}
title={sysAnnouncement.title}
message={sysAnnouncement.message}
/>
)}
<Suspense fallback={<LoadingSpinner />}>
Expand Down
12 changes: 12 additions & 0 deletions client/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import axios, { AxiosRequestConfig } from "axios";
import {
Announcement,
AssetFilters,
AssetSearchParams,
AssetTagFramework,
Expand Down Expand Up @@ -36,6 +37,17 @@ import { CloudflareCaptionData } from "./types/Misc";
*/

class API {
// ANNOUNCEMENTS
async getSystemAnnouncement() {
const res = await axios.get<
{
sysAnnouncement: Announcement | null;
} & ConductorBaseResponse
>("/announcements/system");

return res;
}

// ASSET TAGS FRAMEWORKS
async getFrameworks({
page,
Expand Down
26 changes: 26 additions & 0 deletions client/src/hooks/useSystemAnnouncement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useQuery } from "@tanstack/react-query";
import { Announcement } from "../types";
import api from "../api";

const useSystemAnnouncement = () => {
const { data, isFetching } = useQuery<Announcement | null>({
queryKey: ["system-announcement"],
queryFn: async () => {
try {
const res = await api.getSystemAnnouncement();
if (!res.data) throw new Error("No data returned from the server.");
return res.data?.sysAnnouncement ?? null;
} catch (error) {
console.error(error); // fail silently
return null;
}
},
staleTime: 1000 * 60 * 3, // 3 minutes
refetchOnMount: false,
refetchOnWindowFocus: false,
});

return { sysAnnouncement: data, loading: isFetching };
};

export default useSystemAnnouncement;
35 changes: 5 additions & 30 deletions client/src/screens/conductor/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import useGlobalError from "../../../components/error/ErrorHooks";
import { pinProject } from "../../../utils/projectHelpers";
import Annnouncement from "../../../components/Home/Announcement";
import UserMenu from "../../../components/Home/UserMenu";
import useSystemAnnouncement from "../../../hooks/useSystemAnnouncement";
const NewMemberModal = lazy(
() => import("../../../components/Home/NewMemberModal")
);
Expand All @@ -44,6 +45,7 @@ const Home = () => {
const { handleGlobalError } = useGlobalError();
const location = useLocation();
const user = useTypedSelector((state) => state.user);
const { sysAnnouncement } = useSystemAnnouncement();

/* Data */
const [announcements, setAnnouncements] = useState<Announcement[]>([]);
Expand All @@ -57,12 +59,6 @@ const Home = () => {
const [loadedAllPinned, setLoadedAllPinned] = useState<boolean>(false);
const [showNASuccess, setShowNASuccess] = useState<boolean>(false);

// System Announcement Message
const [showSystemAnnouncement, setShowSystemAnnouncement] =
useState<boolean>(false);
const [systemAnnouncementData, setSystemAnnouncementData] =
useState<Announcement>({} as Announcement);

// New Announcement Modal
const [showNewAnnounceModal, setShowNewAnnounceModal] =
useState<boolean>(false);
Expand Down Expand Up @@ -92,25 +88,6 @@ const Home = () => {
}
}, [location.search, setShowNMModal]);

/**
* Checks if a System Announcement is available and updates the UI accordingly if so.
*/
const getSystemAnnouncement = useCallback(async () => {
try {
const res = await axios.get("/announcements/system");
if (res.data.err) {
throw new Error(res.data.errMsg);
}

if (res.data.sysAnnouncement !== null) {
setShowSystemAnnouncement(true);
setSystemAnnouncementData(res.data.sysAnnouncement);
}
} catch (err) {
console.error(err); // fail silently
}
}, [setShowSystemAnnouncement, setSystemAnnouncementData]);

/**
* Loads the 5 most recent announcements via GET
* request and updates the UI accordingly.
Expand Down Expand Up @@ -194,12 +171,10 @@ const Home = () => {
});
getPinnedProjects();
getRecentProjects();
getSystemAnnouncement();
getAnnouncements();
}, [
getPinnedProjects,
getRecentProjects,
getSystemAnnouncement,
getAnnouncements,
]);

Expand Down Expand Up @@ -241,13 +216,13 @@ const Home = () => {
<Header className="component-header">Home</Header>
<div className="border border-b-gray-300 w-full"></div>
</div>
{showSystemAnnouncement && (
{sysAnnouncement && (
<div className="flex w-full mb-4">
<Message icon info>
<Icon name="info circle" />
<Message.Content>
<Message.Header>{systemAnnouncementData.title}</Message.Header>
<p>{systemAnnouncementData.message}</p>
<Message.Header>{sysAnnouncement.title}</Message.Header>
<p>{sysAnnouncement.message}</p>
</Message.Content>
</Message>
</div>
Expand Down
70 changes: 41 additions & 29 deletions client/src/screens/conductor/support/SupportCreateTicket.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { Image } from "semantic-ui-react";
import DefaultLayout from "../../../components/kb/DefaultLayout";
import { useEffect, useState } from "react";
import { useTypedSelector } from "../../../state/hooks";
import AuthHelper from "../../../components/util/AuthHelper";
import CreateTicketFlow from "../../../components/support/CreateTicketFlow";
import useSystemAnnouncement from "../../../hooks/useSystemAnnouncement";
import SystemAnnouncement from "../../../components/util/SystemAnnouncement";

const SupportCreateTicket = () => {
const user = useTypedSelector((state) => state.user);
const [guestMode, setGuestMode] = useState(false);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const { sysAnnouncement } = useSystemAnnouncement();

const checkIsLoggedIn = () => {
if (user && user.uuid) {
Expand All @@ -33,35 +35,45 @@ const SupportCreateTicket = () => {

return (
<DefaultLayout altBackground>
<div className="flex flex-col w-full min-h-[80vh] items-center justify-center">
<div className="flex flex-col w-full items-center mt-4">
<h1 className="text-4xl font-semibold">Contact Support</h1>
<p className="mt-2">Submit a support ticket to get help from our team.</p>
<>
{!isLoggedIn && !guestMode && (
<div className="flex flex-col w-2/5 mt-12 items-center">
<button
onClick={() => redirectToLogin()}
className="w-3/4 h-12 flex bg-primary rounded-md text-white text-lg my-2 items-center justify-center shadow-md hover:shadow-xl"
>
Log In with LibreOne (Recommended)
</button>
<button
onClick={() => setGuestMode(true)}
className="w-3/4 h-12 flex bg-primary rounded-md text-white text-lg my-2 items-center justify-center shadow-md hover:shadow-xl"
>
Continue as Guest
</button>
</div>
)}
{(isLoggedIn || guestMode) && (
<div className="mt-4 flex w-full justify-center">
<CreateTicketFlow isLoggedIn={isLoggedIn} />
</div>
)}
</>
<>
{sysAnnouncement && (
<SystemAnnouncement
title={sysAnnouncement.title}
message={sysAnnouncement.message}
/>
)}
<div className="flex flex-col w-full min-h-[80vh] items-center justify-center">
<div className="flex flex-col w-full items-center mt-4">
<h1 className="text-4xl font-semibold">Contact Support</h1>
<p className="mt-2">
Submit a support ticket to get help from our team.
</p>
<>
{!isLoggedIn && !guestMode && (
<div className="flex flex-col w-2/5 mt-12 items-center">
<button
onClick={() => redirectToLogin()}
className="w-3/4 h-12 flex bg-primary rounded-md text-white text-lg my-2 items-center justify-center shadow-md hover:shadow-xl"
>
Log In with LibreOne (Recommended)
</button>
<button
onClick={() => setGuestMode(true)}
className="w-3/4 h-12 flex bg-primary rounded-md text-white text-lg my-2 items-center justify-center shadow-md hover:shadow-xl"
>
Continue as Guest
</button>
</div>
)}
{(isLoggedIn || guestMode) && (
<div className="mt-4 flex w-full justify-center">
<CreateTicketFlow isLoggedIn={isLoggedIn} />
</div>
)}
</>
</div>
</div>
</div>
</>
</DefaultLayout>
);
};
Expand Down

0 comments on commit 0b5f5cb

Please sign in to comment.