Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Work for 10/18 #255

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/comps/admin/AdminNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import EmailIcon from "@mui/icons-material/Email";
import CampaignIcon from "@mui/icons-material/Campaign";
import MeetingRoomIcon from "@mui/icons-material/MeetingRoom";
import { Box } from "@mui/material";
import { Send } from "@mui/icons-material";

const AdminNav = () => {
let navLinks = [
Expand Down Expand Up @@ -39,6 +40,11 @@ const AdminNav = () => {
label: "Rooms",
icon: <MeetingRoomIcon />,
},
{
to: "/admin/club-admin-emails",
label: "Admin Emails",
icon: <Send />,
},
];

return (
Expand Down
2 changes: 2 additions & 0 deletions src/comps/pages/orgs/OrgNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import CalendarMonthIcon from "@mui/icons-material/CalendarMonth";
import PeopleIcon from "@mui/icons-material/People";
import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettings";
import AsyncButton from "../../ui/AsyncButton";
import { PostAdd } from "@mui/icons-material";

const OrgNav = ({ isMobile }: { isMobile: boolean }) => {
const organization = useContext<OrgContextType>(OrgContext);
Expand All @@ -44,6 +45,7 @@ const OrgNav = ({ isMobile }: { isMobile: boolean }) => {
icon: <CalendarMonthIcon />,
},
{ to: `${main}/members`, display: "Members", icon: <PeopleIcon /> },
{ to: `${main}/posts`, display: "Posts", icon: <PostAdd /> },
];

const [currentIndex, setCurrentIndex] = useState(0);
Expand Down
84 changes: 52 additions & 32 deletions src/comps/pages/orgs/admin/PendingMember.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { supabase } from "../../../../supabaseClient";
import { Box } from "@mui/material";
import { useSnackbar } from "notistack";
import { useContext } from "react";
import { useContext, useEffect } from "react";
import OrgContext from "../../../context/OrgContext";
import OrgMember from "../OrgMember";
import AsyncButton from "../../../ui/AsyncButton";
Expand All @@ -12,12 +12,14 @@ const PendingMember = ({
last_name,
email,
picture,
auto,
}: {
id: number;
first_name?: string;
last_name?: string;
email: string;
picture: string | undefined;
auto: boolean;
}) => {
const { enqueueSnackbar } = useSnackbar();
const organization = useContext(OrgContext);
Expand Down Expand Up @@ -83,39 +85,57 @@ const PendingMember = ({
enqueueSnackbar("User rejected!", { variant: "success" });
};

useEffect(() => {
if (auto) {
handleApprove();
TheEgghead27 marked this conversation as resolved.
Show resolved Hide resolved
}
}, [auto]);

return (
<Box
sx={{
width: "100%",
display: "flex",
flexWrap: "nowrap",
alignItems: "center",
}}
>
<Box sx={{ width: "100%" }}>
<OrgMember
email={email}
picture={picture}
first_name={first_name}
last_name={last_name}
/>
</Box>
<Box sx={{ width: "200px", display: "flex", flexWrap: "nowrap" }}>
<AsyncButton
onClick={handleApprove}
variant="contained"
sx={{ height: "40px" }}
>
Approve
</AsyncButton>
<AsyncButton
onClick={handleReject}
variant="contained"
sx={{ height: "40px", marginLeft: "10px" }}
<Box>
{!auto ? (
<Box
sx={{
width: "100%",
display: "flex",
flexWrap: "nowrap",
alignItems: "center",
}}
>
Reject
</AsyncButton>
</Box>
<Box sx={{ width: "100%" }}>
<OrgMember
email={email}
picture={picture}
first_name={first_name}
last_name={last_name}
/>
</Box>
<Box
sx={{
width: "200px",
display: "flex",
flexWrap: "nowrap",
}}
>
<AsyncButton
onClick={handleApprove}
variant="contained"
sx={{ height: "40px" }}
>
Approve
</AsyncButton>
<AsyncButton
onClick={handleReject}
variant="contained"
sx={{ height: "40px", marginLeft: "10px" }}
>
Reject
</AsyncButton>
</Box>
</Box>
) : (
<></>
)}
</Box>
);
};
Expand Down
218 changes: 218 additions & 0 deletions src/pages/admin/ClubAdminEmails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import { useSnackbar } from "notistack";
import { useContext, useEffect, useState } from "react";
import { supabase } from "../../supabaseClient";
import { Box, TextField, Typography } from "@mui/material";
import AsyncButton from "../../comps/ui/AsyncButton";
import UserContext from "../../comps/context/UserContext";

const ClubAdminEmails = () => {
const { enqueueSnackbar } = useSnackbar();

const user = useContext(UserContext);

const [orgId, setOrgId] = useState<number>();
const [orgName, setOrgName] = useState("");
const [searchInput, setSearchInput] = useState("");
const [filteredOrgs, setFilteredOrgs] = useState<Organization[]>([]);
const [allOrgs, setAllOrgs] = useState<Organization[]>([]);
const [currentOrganization, setCurrentOrganization] =
useState<Organization>();
const [adminEmails, setAdminEmails] = useState<string>("");

useEffect(() => {
const fetchAllOrgs = async () => {
const { data, error } = await supabase
.from("organizations")
.select("*");
if (error || !data) {
enqueueSnackbar(
"Failed to load organizations. Contact [email protected] for support.",
{ variant: "error" },
);
return;
}
setAllOrgs(data);
};

fetchAllOrgs();
}, [enqueueSnackbar]);

useEffect(() => {
if (searchInput) {
setFilteredOrgs(
allOrgs.filter((org) =>
org.name.toLowerCase().includes(searchInput.toLowerCase()),
),
);
} else {
setFilteredOrgs([]);
}
}, [searchInput, allOrgs]);

// useEffect(() => {
TheEgghead27 marked this conversation as resolved.
Show resolved Hide resolved
// if(currentOrganization) {
// const emails = currentOrganization?.memberships
// ?.filter((membership) => membership.role === "ADMIN" || membership.role === "CREATOR")
// .map((membership) => membership.users?.email)
// .join(", ");

// setAdminEmails(emails || "");
// }
// }, [currentOrganization]);

useEffect(() => {
const fetchAdminEmails = async () => {
const { data: memberships, error: membershipsError } =
await supabase
.from("memberships")
.select("user_id")
.eq("organization_id", currentOrganization?.id)
.in("role", ["ADMIN", "CREATOR"]);

if (membershipsError) {
enqueueSnackbar(
"Error fetching memberships. Please try again later.",
{ variant: "error" },
);
return;
}
const userIds = memberships.map((membership) => membership.user_id);

const { data: users, error: usersError } = await supabase
.from("users")
.select("email")
.in("id", userIds);

if (usersError) {
console.error("Error fetching users:", { variant: "error" });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in a snackbar so the end user knows what went wrong?

return;
}

const adminEmails = users.map((user) => user.email).join(", ");
setAdminEmails(adminEmails);
};
if (currentOrganization) {
fetchAdminEmails();
}
}, [currentOrganization]);

return (
<Box>
<Typography variant="h1" align="center">
Club Admin Emails
</Typography>
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
marginTop: "20px",
}}
>
<TextField
sx={{ width: "300px" }}
label="Search Organizations"
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
/>
</Box>
{filteredOrgs.length > 0 && (
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
marginTop: "10px",
flexWrap: "wrap",
}}
>
{filteredOrgs.map((org) => (
<AsyncButton
key={org.id}
onClick={() => {
setOrgId(org.id);
setOrgName(org.name);
setCurrentOrganization(org);
setSearchInput("");
setFilteredOrgs([]);
}}
sx={{ margin: "5px" }}
>
{org.name}
</AsyncButton>
))}
</Box>
)}
{orgId && (
<>
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
flexWrap: "wrap",
}}
>
<Typography variant="h2" width="100%" align="center">
Org: {orgName}
</Typography>
<Box
sx={{
width: "100%",
display: "flex",
flexWrap: "nowrap",
alignItems: "center",
paddingLeft: "16px",
paddingRight: "16px",
marginLeft: "4em",
marginRight: "4em",
}}
>
<Box
sx={{
paddingTop: "8px",
paddingBottom: "8px",
width: "100%",
}}
>
<TextField
disabled
fullWidth
value={adminEmails}
variant="outlined"
/>
</Box>
<Box sx={{ paddingLeft: "16px", width: "100px" }}>
<AsyncButton
onClick={async () => {
try {
await navigator.clipboard.writeText(
adminEmails,
);
enqueueSnackbar(
"Copied emails to clipboard!",
{
variant: "success",
},
);
} catch (error) {
enqueueSnackbar(
"Failed to copy emails to clipboard. :( Try manually copying from the page.",
{ variant: "error" },
);
}
}}
variant="contained"
>
Copy
</AsyncButton>
</Box>
</Box>
</Box>
</>
)}
</Box>
);
};

export default ClubAdminEmails;
4 changes: 2 additions & 2 deletions src/pages/admin/Strikes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ const Strikes = () => {
onClick={() => {
setOrgId(org.id);
setOrgName(org.name);
setSearchInput(""); // Clear search input after selecting an org
setFilteredOrgs([]); // Clear filtered orgs after selecting an org
setSearchInput("");
setFilteredOrgs([]);
}}
sx={{ margin: "5px" }}
>
Expand Down
2 changes: 2 additions & 0 deletions src/pages/admin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Strikes from "./Strikes";
import SendMessage from "./SendMessage";
import Announcements from "./Announcements";
import Rooms from "./Rooms";
import ClubAdminEmails from "./ClubAdminEmails";

const AdminRouter = () => {
const user = useContext(UserContext);
Expand All @@ -30,6 +31,7 @@ const AdminRouter = () => {
<Route path="/send-message" Component={SendMessage} />
<Route path="/announcements" Component={Announcements} />
<Route path="/rooms" Component={Rooms} />
<Route path="/club-admin-emails" Component={ClubAdminEmails} />
<Route path="/*" Component={ApprovePending} />
</Routes>
</div>
Expand Down
Loading