Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file was deleted.

3 changes: 3 additions & 0 deletions frontend/src/Pages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ import ApiKeyDetails from "./components/apiKeys/ApiKeyDetails.tsx";
import DomainDetails from "./components/domains/DomainDetails.tsx";
import GlobalAdmin from "./components/admin/GlobalAdmin.tsx";
import PasswordReset from "./PasswordReset.tsx";
import { EmailOverview } from "./components/emails/EmailOverview.tsx";

const PageContent: { [key in RouteName]: JSX.Element | null } = {
emails: <EmailOverview />,
"emails.email": <EmailDetails />,
projects: <ProjectsOverview />,
"projects.project": <ProjectDetails />,
"projects.project.emails": <ProjectDetails />,
Expand Down
18 changes: 11 additions & 7 deletions frontend/src/apiMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ export default async function apiMiddleware(
type: "set_domains",
domains: await get(`/api/organizations/${newOrgId}/domains`),
});
dispatch({
type: "set_labels",
labels: await get(`/api/organizations/${newOrgId}/emails/labels`),
});
}

if (navState.to.name == "statistics") {
Expand All @@ -115,23 +119,23 @@ export default async function apiMiddleware(
type: "set_credentials",
credentials: await get(`/api/organizations/${newOrgId}/projects/${newProjId}/smtp_credentials`),
});
dispatch({
type: "set_labels",
labels: await get(`/api/organizations/${newOrgId}/projects/${newProjId}/labels`),
});
}

let emailFilterChanged = false;
const emailFilter = new URLSearchParams();
for (const param of ["limit", "status", "before", "labels"]) {
for (const param of ["limit", "status", "before", "labels", "project"]) {
const value = navState.to.params[param];
if (value != navState.from.params[param]) emailFilterChanged = true;
if (value) emailFilter.append(param, value);
}
if ((projChanged || emailFilterChanged || navState.to.params.force == "reload") && newProjId) {
if (navState.to.params.proj_id) {
// force project filter when path contains a project id (for viewing emails within a project)
emailFilter.set("project", navState.to.params.proj_id);
}
if (projChanged || emailFilterChanged || navState.to.name == "emails" || navState.to.params.force == "reload") {
dispatch({
type: "set_emails",
emailMetadata: await get(`/api/organizations/${newOrgId}/projects/${newProjId}/emails?${emailFilter.toString()}`),
emailMetadata: await get(`/api/organizations/${newOrgId}/emails?${emailFilter.toString()}`),
});
}

Expand Down
21 changes: 21 additions & 0 deletions frontend/src/components/ProjectLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Group } from "@mantine/core";
import { useProjectWithId } from "../hooks/useProjects";
import { Link } from "../Link";
import { IconServer } from "@tabler/icons-react";

interface ProjectLinkProps {
project_id: string;
size?: "md" | "sm";
}

export default function ProjectLink({ project_id, size }: ProjectLinkProps) {
const project_name = useProjectWithId(project_id)?.name;

return (
<Link to={"projects.project"} params={{ proj_id: project_id }} style={{ size }}>
<Group gap="0.4em">
<IconServer size={size == "sm" ? 20 : 24} /> {project_name ?? project_id}
</Group>
</Link>
);
}
14 changes: 4 additions & 10 deletions frontend/src/components/domains/DomainsOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useDomains } from "../../hooks/useDomains.ts";
import { Loader } from "../../Loader.tsx";
import { Flex, Group, Pagination, Stack, Table, Text } from "@mantine/core";
import { Flex, Pagination, Stack, Table, Text } from "@mantine/core";
import { formatDateTime } from "../../util.ts";
import { IconPlus, IconServer } from "@tabler/icons-react";
import { IconPlus } from "@tabler/icons-react";
import { useDisclosure } from "@mantine/hooks";
import { NewDomain } from "./NewDomain.tsx";
import { Link } from "../../Link.tsx";
Expand All @@ -12,18 +12,16 @@ import VerificationBadge from "./VerificationBadge.tsx";
import EditButton from "../EditButton.tsx";
import OrganizationHeader from "../organizations/OrganizationHeader.tsx";
import { MaintainerButton } from "../RoleButtons.tsx";
import { useProjectWithId } from "../../hooks/useProjects.ts";
import { Domain } from "../../types.ts";
import { useRemails } from "../../hooks/useRemails.ts";
import { useState } from "react";
import SearchInput from "../SearchInput.tsx";
import ProjectLink from "../ProjectLink.tsx";

const PER_PAGE = 20;
const SHOW_SEARCH = 10;

function DomainRow({ domain }: { domain: Domain }) {
const project_name = useProjectWithId(domain.project_id)?.name;

return (
<Table.Tr>
<Table.Td>
Expand All @@ -38,11 +36,7 @@ function DomainRow({ domain }: { domain: Domain }) {
</Table.Td>
<Table.Td>
{domain.project_id ? (
<Link to={"projects.project"} params={{ proj_id: domain.project_id }}>
<Group gap="0.4em">
<IconServer /> {project_name ?? domain.project_id}
</Group>
</Link>
<ProjectLink project_id={domain.project_id} />
) : (
<Text fs="italic" c="dimmed">
any project
Expand Down
13 changes: 4 additions & 9 deletions frontend/src/components/emails/EmailDeleteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,23 @@ import { IconTrash } from "@tabler/icons-react";
import { EmailMetadata } from "../../types.ts";
import { modals } from "@mantine/modals";
import { useOrganizations } from "../../hooks/useOrganizations.ts";
import { useProjects } from "../../hooks/useProjects.ts";
import { notifications } from "@mantine/notifications";
import { useRemails } from "../../hooks/useRemails.ts";
import { errorNotification } from "../../notify.tsx";
import { MaintainerActionIcon, MaintainerButton } from "../RoleButtons.tsx";

export default function EmailDeleteButton({ email, small }: { email: EmailMetadata; small?: boolean }) {
const { currentOrganization } = useOrganizations();
const { currentProject } = useProjects();
const { navigate, dispatch } = useRemails();

if (!currentOrganization || !currentProject) {
if (!currentOrganization) {
return null;
}

const deleteEmail = async () => {
const res = await fetch(
`/api/organizations/${currentOrganization.id}/projects/${currentProject.id}/emails/${email.id}`,
{
method: "DELETE",
}
);
const res = await fetch(`/api/organizations/${currentOrganization.id}/emails/${email.id}`, {
method: "DELETE",
});
if (res.status === 200) {
notifications.show({
title: "Email deleted",
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/emails/EmailDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import { useState } from "react";
import { Loader } from "../../Loader.tsx";
import { Email, EmailMetadata } from "../../types.ts";
import { formatDateTime, is_in_the_future } from "../../util.ts";
import { IconHelp, IconMessage, IconPaperclip } from "@tabler/icons-react";
import { IconHelp, IconMail, IconPaperclip } from "@tabler/icons-react";
import EmailRetryButton from "./EmailRetryButton.tsx";
import EmailDeleteButton from "./EmailDeleteButton.tsx";
import { Recipients } from "./Recipients.tsx";
import Header from "../Header.tsx";
import Label from "./Label.tsx";
import ProjectLink from "../ProjectLink.tsx";

export function getFullStatusDescription(email: EmailMetadata) {
if (email.status == "delivered") {
Expand Down Expand Up @@ -60,6 +61,7 @@ export default function EmailDetails() {
),
},
{ header: "From", value: fullEmail.from_email },
{ header: "Project", value: <ProjectLink project_id={fullEmail.project_id} size="sm" /> },
{
header: "Recipients",
info: 'The recipients who will receive this email based on the "RCPT TO" SMTP header',
Expand Down Expand Up @@ -125,7 +127,7 @@ export default function EmailDetails() {
<Header
name={subject ?? "No subject"}
entityType="Email"
Icon={IconMessage}
Icon={IconMail}
divider
addendum={currentEmail.label ? <Label label={currentEmail.label} clickable /> : null}
/>
Expand Down
Loading
Loading