diff --git a/src/components/Dashboard/Agents/AgentCardList.tsx b/src/components/Dashboard/Agents/AgentCardList.tsx
index 3cbc439d..f0fd84fa 100644
--- a/src/components/Dashboard/Agents/AgentCardList.tsx
+++ b/src/components/Dashboard/Agents/AgentCardList.tsx
@@ -2,6 +2,8 @@ import type { ApiAgentGetList, OptionItem } from "need4deed-sdk";
import { PaginatedGrid } from "@/components/core/paginatedGrid";
import { AgentCard } from "./AgentCard";
import { AgentCardListContainer } from "./styles";
+import { AgentReadOnlyCard } from "./AgentReadOnlyCard";
+import { useAuth } from "@/hooks/useAuth";
type Props = {
agents: ApiAgentGetList[];
@@ -14,7 +16,15 @@ type Props = {
};
export function AgentCardList({ agents, count, columns, rows, currentPage, setCurrentPage, districtsList }: Props) {
- const items = agents.map((agent) => );
+ const { isAuthorized } = useAuth();
+
+ const items = agents.map((agent) =>
+ isAuthorized ? (
+
+ ) : (
+
+ ),
+ );
return (
diff --git a/src/components/Dashboard/Agents/AgentReadOnlyCard.tsx b/src/components/Dashboard/Agents/AgentReadOnlyCard.tsx
new file mode 100644
index 00000000..24c7d270
--- /dev/null
+++ b/src/components/Dashboard/Agents/AgentReadOnlyCard.tsx
@@ -0,0 +1,38 @@
+import { MapPinIcon } from "@phosphor-icons/react";
+import { IconDiv } from "@/components/styled/container";
+import { Heading4, Paragraph } from "@/components/styled/text";
+import { Card, CardDetailsInfo, CardHeader, CardHeaderInfo, DistrictContainer, DistrictDiv } from "./styles";
+import { useTranslation } from "react-i18next";
+import { ApiAgentGetList, OptionItem } from "need4deed-sdk";
+
+interface Props {
+ agent: ApiAgentGetList;
+ districtsList?: OptionItem[];
+}
+
+export const AgentReadOnlyCard = ({ agent, districtsList }: Props) => {
+ const { t } = useTranslation();
+ const { title, district, type } = agent;
+ const districtTitle = district?.id ? (districtsList?.find((d) => d.id === district.id)?.title ?? null) : null;
+ return (
+
+
+
+ {title}
+
+
+
+ {t("dashboard.agentProfile.type")}
+ {type}
+
+
+
+
+
+
+ {districtTitle}
+
+
+
+ );
+};
diff --git a/src/components/Dashboard/Agents/AgentReadOnlyTableRow.tsx b/src/components/Dashboard/Agents/AgentReadOnlyTableRow.tsx
new file mode 100644
index 00000000..9f3b7113
--- /dev/null
+++ b/src/components/Dashboard/Agents/AgentReadOnlyTableRow.tsx
@@ -0,0 +1,22 @@
+import { ApiAgentGetList, OptionItem } from "need4deed-sdk";
+import { ClickableRow, TableCell } from "@/components/core/common/Table";
+
+interface Props {
+ agent: ApiAgentGetList;
+ isLast: boolean;
+ typeLabels: Record;
+ searchLabels: Record;
+ districtsList?: OptionItem[];
+}
+
+export function AgentReadOnlyTableRow({ agent, isLast, districtsList }: Props) {
+ const { id, title, type, district } = agent;
+ const districtTitle = district?.id ? (districtsList?.find((d) => d.id === district.id)?.title ?? null) : null;
+ return (
+
+ {title}
+ {type}
+ {districtTitle || "—"}
+
+ );
+}
diff --git a/src/components/Dashboard/Agents/AgentTableList.tsx b/src/components/Dashboard/Agents/AgentTableList.tsx
index 2a428c54..7f24cfdd 100644
--- a/src/components/Dashboard/Agents/AgentTableList.tsx
+++ b/src/components/Dashboard/Agents/AgentTableList.tsx
@@ -3,10 +3,12 @@
import type { ApiAgentGetList, OptionItem } from "need4deed-sdk";
import { EntityTableList } from "../common/EntityTableList";
import { useTranslation } from "react-i18next";
-import { createAgentTableColumns } from "./agentsTableColumns";
+import { createAgentTableColumns, createReadOnlyAgentTableColumns } from "./agentsTableColumns";
import { useMemo } from "react";
import { AgentTableRow } from "./AgentTableRow";
import { createAgentTypeMap, createVolunteerSearchMap } from "./constants";
+import { AgentReadOnlyTableRow } from "./AgentReadOnlyTableRow";
+import { useAuth } from "@/hooks/useAuth";
interface TableListProps {
agents: ApiAgentGetList[];
@@ -26,30 +28,44 @@ export function AgentTableList({
districtsList,
}: TableListProps) {
const { t } = useTranslation();
+ const { isAuthorized } = useAuth();
const columns = useMemo(() => createAgentTableColumns(t), [t]);
+ const readOnlyColumns = useMemo(() => createReadOnlyAgentTableColumns(t), [t]);
const typeLabels = useMemo(() => createAgentTypeMap(t), [t]);
const searchLabels = useMemo(() => createVolunteerSearchMap(t), [t]);
return (
(
-
- )}
+ renderRow={(agent, isLast) =>
+ isAuthorized ? (
+
+ ) : (
+
+ )
+ }
count={count}
itemsPerPage={itemsPerPage}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
testIdPrefix="agents"
+ noFixedWidth={!isAuthorized}
/>
);
}
diff --git a/src/components/Dashboard/Agents/Filters/FiltersContent.tsx b/src/components/Dashboard/Agents/Filters/FiltersContent.tsx
index 567c0d92..89a22f27 100644
--- a/src/components/Dashboard/Agents/Filters/FiltersContent.tsx
+++ b/src/components/Dashboard/Agents/Filters/FiltersContent.tsx
@@ -4,6 +4,7 @@ import AccordionFilter from "../../common/CardsFilter/AccordionFilter";
import { SetFilter } from "../../common/CardsFilter/types";
import { createAgentFilterItems } from "./helpers";
import { FiltersContentContainer } from "./styles";
+import { useAuth } from "@/hooks/useAuth";
type Props = {
filter: AgentCardsFilter;
@@ -12,6 +13,7 @@ type Props = {
export default function FiltersContent({ setFilter, filter }: Props) {
const { t } = useTranslation();
+ const { isAuthorized } = useAuth();
const { districtFilters, volunteerSearchFilters, typeFilters, engagementStatusFilters, servicesFilters } =
createAgentFilterItems(filter, setFilter, t);
@@ -19,7 +21,9 @@ export default function FiltersContent({ setFilter, filter }: Props) {
return (
-
+ {isAuthorized && (
+
+ )}
diff --git a/src/components/Dashboard/Agents/agentsTableColumns.ts b/src/components/Dashboard/Agents/agentsTableColumns.ts
index 95a5db20..23686f40 100644
--- a/src/components/Dashboard/Agents/agentsTableColumns.ts
+++ b/src/components/Dashboard/Agents/agentsTableColumns.ts
@@ -11,6 +11,12 @@ export const AGENT_COL_WIDTHS = {
email: COLUMN_WIDTH.LG,
};
+export const AGENT_READ_ONLY_COL_WIDTHS = {
+ title: COLUMN_WIDTH.XXXL,
+ type: COLUMN_WIDTH.XXXL,
+ district: COLUMN_WIDTH.XXXL,
+};
+
export const createAgentTableColumns = (t: TFunction): Column[] => [
{ key: "title", label: t("dashboard.agents.table.title") },
{ key: "type", label: t("dashboard.agents.table.type"), width: AGENT_COL_WIDTHS.type },
@@ -32,3 +38,9 @@ export const createAgentTableColumns = (t: TFunction): Column[] => [
},
{ key: "email", label: t("dashboard.agents.table.email") },
];
+
+export const createReadOnlyAgentTableColumns = (t: TFunction): Column[] => [
+ { key: "title", label: t("dashboard.agents.table.title"), width: AGENT_READ_ONLY_COL_WIDTHS.title },
+ { key: "type", label: t("dashboard.agents.table.type"), width: AGENT_READ_ONLY_COL_WIDTHS.type },
+ { key: "district", label: t("dashboard.agents.table.district"), width: AGENT_READ_ONLY_COL_WIDTHS.district },
+];
diff --git a/src/components/Dashboard/Agents/styles.ts b/src/components/Dashboard/Agents/styles.ts
index 08a069a9..73422bb8 100644
--- a/src/components/Dashboard/Agents/styles.ts
+++ b/src/components/Dashboard/Agents/styles.ts
@@ -23,7 +23,7 @@ export const AgentCardListContainer = styled.div`
justify-content: left;
`;
-export const Card = styled(BaseCard)`
+export const Card = styled(BaseCard)<{ $cursor?: string }>`
background-color: var(--color-orchid-subtle);
background-color: var(--color-orchid-subtle);
width: var(--dashboard-agents-card-width);
@@ -33,7 +33,7 @@ export const Card = styled(BaseCard)`
transition:
transform 0.3s ease-in-out,
box-shadow 0.3s ease-in-out;
- cursor: pointer;
+ cursor: ${(props) => props.$cursor || "pointer"};
&:hover {
background-color: var(--color-orchid);
diff --git a/src/components/Dashboard/Opportunities/Filters/FiltersContent.tsx b/src/components/Dashboard/Opportunities/Filters/FiltersContent.tsx
index 016febbd..a42dcc3c 100644
--- a/src/components/Dashboard/Opportunities/Filters/FiltersContent.tsx
+++ b/src/components/Dashboard/Opportunities/Filters/FiltersContent.tsx
@@ -4,6 +4,7 @@ import AccordionFilter from "../../common/CardsFilter/AccordionFilter";
import { SetFilter } from "../../common/CardsFilter/types";
import { createOpportunityFilterItems } from "./helpers";
import { FiltersContentContainer } from "./styles";
+import { useAuth } from "@/hooks/useAuth";
type Props = {
filter: OpportunityCardsFilter;
@@ -12,6 +13,7 @@ type Props = {
export default function FiltersContent({ setFilter, filter }: Props) {
const { t } = useTranslation();
+ const { isAuthorized } = useAuth();
const { districtFilters, languageFilters, statusFilters, typeFilters, activityFilters, availabilityFilters } =
createOpportunityFilterItems(filter, setFilter, t);
@@ -22,11 +24,13 @@ export default function FiltersContent({ setFilter, filter }: Props) {
-
+ {isAuthorized && (
+
+ )}
);
}
diff --git a/src/components/Dashboard/Opportunities/OpportunityCardList.tsx b/src/components/Dashboard/Opportunities/OpportunityCardList.tsx
index 6e825bc5..17916f8b 100644
--- a/src/components/Dashboard/Opportunities/OpportunityCardList.tsx
+++ b/src/components/Dashboard/Opportunities/OpportunityCardList.tsx
@@ -2,6 +2,8 @@ import { ApiVolunteerOpportunityGetList, OptionItem } from "need4deed-sdk";
import { PaginatedGrid } from "@/components/core/paginatedGrid";
import { OpportunityCard } from "./OpportunityCard";
import { OpportunityCardListContainer } from "./styles";
+import { OpportunityReadOnlyCard } from "./OpportunityReadOnlyCard";
+import { useAuth } from "@/hooks/useAuth";
type Props = {
activitiesList?: OptionItem[];
@@ -26,15 +28,21 @@ export function OpportunityCardList({
activitiesList,
districtsList,
}: Props) {
- const items = opportunities.map((opp) => (
-
- ));
+ const { isAuthorized } = useAuth();
+
+ const items = opportunities.map((opp) =>
+ isAuthorized ? (
+
+ ) : (
+
+ ),
+ );
return (
diff --git a/src/components/Dashboard/Opportunities/OpportunityReadOnlyCard.tsx b/src/components/Dashboard/Opportunities/OpportunityReadOnlyCard.tsx
new file mode 100644
index 00000000..566bce18
--- /dev/null
+++ b/src/components/Dashboard/Opportunities/OpportunityReadOnlyCard.tsx
@@ -0,0 +1,83 @@
+import { ApiVolunteerOpportunityGetList, LangPurpose, OptionItem, ProfileVolunteeringType } from "need4deed-sdk";
+import { useTranslation } from "react-i18next";
+import { Paragraph } from "@/components/styled/text";
+import CardDetail from "../Volunteers/CardDetail";
+import { CardParagraph } from "../Volunteers/VolunteerCard";
+import { IconName } from "../Volunteers/icon";
+import { matchStatusColorMap, matchStatusIconMap, volunteerTypeIconMap } from "./OpportunityCard.helpers";
+import { Card, LanguageRow, StatusDiv, StatusTagsDiv, TagDiv, TitleParagraph } from "./styles";
+import { getLanguagesByPurpose } from "./helpers";
+
+type Props = {
+ opportunity: ApiVolunteerOpportunityGetList;
+ volunteerId?: string;
+ activitiesList?: OptionItem[];
+ districtsList?: OptionItem[];
+};
+
+export function OpportunityReadOnlyCard({ opportunity, districtsList }: Props) {
+ const { t } = useTranslation();
+
+ const { title, volunteerType, district, languages, statusMatch } = opportunity as ApiVolunteerOpportunityGetList & {
+ accompanyingDetails?: { appointmentDate?: string; appointmentTime?: string };
+ statusMatch?: string;
+ district?: { id: number };
+ };
+
+ const mainCommunication = getLanguagesByPurpose(languages, LangPurpose.GENERAL);
+ const recipientLanguage = getLanguagesByPurpose(languages, LangPurpose.RECIPIENT);
+
+ const districtTitle = district?.id ? (districtsList?.find((d) => d.id === district.id)?.title ?? null) : null;
+ return (
+
+
+ {statusMatch && (
+
+ {matchStatusIconMap[statusMatch]}
+
+ {t(`dashboard.opportunities.matchStatus.${statusMatch}`)}
+
+
+ )}
+ {volunteerType && (
+
+
+ {t(`dashboard.opportunities.type.${volunteerType}`)}
+
+ {volunteerTypeIconMap[volunteerType as ProfileVolunteeringType]}
+
+ )}
+
+
+ {title}
+
+
+ {mainCommunication && (
+
+
+
+
+ )}
+ {recipientLanguage && (
+
+
+
+
+ )}
+
+
+
+ {districtTitle && }
+
+
+ );
+}
diff --git a/src/components/Dashboard/Opportunities/OpportunityReadOnlyTableRow.tsx b/src/components/Dashboard/Opportunities/OpportunityReadOnlyTableRow.tsx
new file mode 100644
index 00000000..b6672f08
--- /dev/null
+++ b/src/components/Dashboard/Opportunities/OpportunityReadOnlyTableRow.tsx
@@ -0,0 +1,44 @@
+"use client";
+
+import { LangPurpose, type ApiVolunteerOpportunityGetList, type OptionItem } from "need4deed-sdk";
+import { useTranslation } from "react-i18next";
+import { ClickableRow, TableCell } from "@/components/core/common/Table";
+import { OPPORTUNITY_READ_ONLY_COL_WIDTHS } from "./opportunitiesTableColumns";
+import { getLanguagesByPurpose } from "./helpers";
+
+interface TableRowProps {
+ opportunity: ApiVolunteerOpportunityGetList;
+ isLast: boolean;
+ activitiesList?: OptionItem[];
+ districtsList?: OptionItem[];
+}
+
+export function OpportunityReadOnlyTableRow({ opportunity, isLast, districtsList }: TableRowProps) {
+ const { t } = useTranslation();
+
+ const { id, title, volunteerType, district, statusMatch, languages } = opportunity;
+ const mainCommunication = getLanguagesByPurpose(languages, LangPurpose.GENERAL);
+ const districtTitle = district?.id ? (districtsList?.find((d) => d.id === district.id)?.title ?? null) : null;
+ return (
+
+
+ {title}
+
+
+ {t(`dashboard.opportunities.type.${volunteerType}`)}
+
+
+ {t(`dashboard.opportunities.matchStatus.${statusMatch}`)}
+
+
+ {mainCommunication || "—"}
+
+
+ {districtTitle || "—"}
+
+
+ );
+}
diff --git a/src/components/Dashboard/Opportunities/OpportunityTableList.tsx b/src/components/Dashboard/Opportunities/OpportunityTableList.tsx
index ce8b296a..0f929015 100644
--- a/src/components/Dashboard/Opportunities/OpportunityTableList.tsx
+++ b/src/components/Dashboard/Opportunities/OpportunityTableList.tsx
@@ -4,8 +4,10 @@ import type { ApiVolunteerOpportunityGetList, OptionItem } from "need4deed-sdk";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { EntityTableList } from "../common/EntityTableList";
-import { createOpportunityTableColumns } from "./opportunitiesTableColumns";
+import { createOpportunityTableColumns, createReadOnlyAgentTableColumns } from "./opportunitiesTableColumns";
import { OpportunityTableRow } from "./OpportunityTableRow";
+import { OpportunityReadOnlyTableRow } from "./OpportunityReadOnlyTableRow";
+import { useAuth } from "@/hooks/useAuth";
interface TableListProps {
opportunities: ApiVolunteerOpportunityGetList[];
@@ -27,22 +29,32 @@ export function OpportunityTableList({
activitiesList,
}: TableListProps) {
const { t } = useTranslation();
+ const { isAuthorized } = useAuth();
const columns = useMemo(() => createOpportunityTableColumns(t), [t]);
-
+ const readOnlyColumns = useMemo(() => createReadOnlyAgentTableColumns(t), [t]);
return (
(
-
- )}
+ renderRow={(opportunity, isLast) =>
+ isAuthorized ? (
+
+ ) : (
+
+ )
+ }
count={count}
itemsPerPage={itemsPerPage}
currentPage={currentPage}
diff --git a/src/components/Dashboard/Opportunities/opportunitiesTableColumns.ts b/src/components/Dashboard/Opportunities/opportunitiesTableColumns.ts
index 1eb64cb1..c2d07c1a 100644
--- a/src/components/Dashboard/Opportunities/opportunitiesTableColumns.ts
+++ b/src/components/Dashboard/Opportunities/opportunitiesTableColumns.ts
@@ -15,6 +15,14 @@ export const OPPORTUNITY_COL_WIDTHS = {
agentTitle: COLUMN_WIDTH.MD,
};
+export const OPPORTUNITY_READ_ONLY_COL_WIDTHS = {
+ title: COLUMN_WIDTH.XXXL,
+ volunteerType: COLUMN_WIDTH.XXXL,
+ statusMatch: COLUMN_WIDTH.XXXL,
+ languages: COLUMN_WIDTH.XXXL,
+ district: COLUMN_WIDTH.XXXL,
+};
+
export const createOpportunityTableColumns = (t: TFunction): Column[] => [
{ key: "title", label: t("dashboard.opportunities.table.title"), width: OPPORTUNITY_COL_WIDTHS.title },
{ key: "volunteerType", label: t("dashboard.opportunities.table.type"), width: OPPORTUNITY_COL_WIDTHS.volunteerType },
@@ -39,3 +47,19 @@ export const createOpportunityTableColumns = (t: TFunction): Column[] => [
},
{ key: "agentTitle", label: t("dashboard.opportunities.table.agentName"), width: OPPORTUNITY_COL_WIDTHS.agentTitle },
];
+
+export const createReadOnlyAgentTableColumns = (t: TFunction): Column[] => [
+ { key: "title", label: t("dashboard.agents.table.title"), width: OPPORTUNITY_READ_ONLY_COL_WIDTHS.title },
+ { key: "type", label: t("dashboard.agents.table.type"), width: OPPORTUNITY_READ_ONLY_COL_WIDTHS.volunteerType },
+ {
+ key: "statusMatch",
+ label: t("dashboard.opportunities.table.matchingStatus"),
+ width: OPPORTUNITY_READ_ONLY_COL_WIDTHS.statusMatch,
+ },
+ {
+ key: "languages",
+ label: t("dashboard.opportunities.table.languages"),
+ width: OPPORTUNITY_READ_ONLY_COL_WIDTHS.languages,
+ },
+ { key: "district", label: t("dashboard.agents.table.district"), width: OPPORTUNITY_READ_ONLY_COL_WIDTHS.district },
+];
diff --git a/src/components/Dashboard/Opportunities/styles.ts b/src/components/Dashboard/Opportunities/styles.ts
index 8d146bb5..2192afae 100644
--- a/src/components/Dashboard/Opportunities/styles.ts
+++ b/src/components/Dashboard/Opportunities/styles.ts
@@ -24,7 +24,7 @@ export const OpportunityCardListContainer = styled.div`
justify-content: left;
`;
-export const Card = styled(BaseCard)`
+export const Card = styled(BaseCard)<{ $cursor?: string }>`
background-color: var(--color-orchid-subtle);
width: var(--dashboard-volunteers-card-width);
min-height: var(--dashboard-volunteers-card-height);
@@ -33,7 +33,7 @@ export const Card = styled(BaseCard)`
transition:
transform 0.3s ease-in-out,
box-shadow 0.3s ease-in-out;
- cursor: pointer;
+ cursor: ${(props) => props.$cursor || "pointer"};
&:hover {
background-color: var(--color-orchid);
diff --git a/src/components/Dashboard/common/EntityTableList/EntityTableList.tsx b/src/components/Dashboard/common/EntityTableList/EntityTableList.tsx
index eb96b422..4617e8eb 100644
--- a/src/components/Dashboard/common/EntityTableList/EntityTableList.tsx
+++ b/src/components/Dashboard/common/EntityTableList/EntityTableList.tsx
@@ -14,6 +14,7 @@ export function EntityTableList({
currentPage,
setCurrentPage,
testIdPrefix,
+ noFixedWidth = false,
}: EntityTableListProps) {
const totalPages = Math.ceil(count / itemsPerPage);
const goToPage = (page: number) => {
@@ -26,7 +27,7 @@ export function EntityTableList({
{columns.map((col) => (
-
+
{col.label}
{col.headerAction}
diff --git a/src/components/Dashboard/common/EntityTableList/types.ts b/src/components/Dashboard/common/EntityTableList/types.ts
index 4302e0bb..de04e214 100644
--- a/src/components/Dashboard/common/EntityTableList/types.ts
+++ b/src/components/Dashboard/common/EntityTableList/types.ts
@@ -16,4 +16,5 @@ export interface EntityTableListProps {
currentPage: number;
setCurrentPage: (page: number) => void;
testIdPrefix: string;
+ noFixedWidth?: boolean;
}
diff --git a/src/components/core/common/Table/styles.ts b/src/components/core/common/Table/styles.ts
index 6a3f77e2..259c5bdb 100644
--- a/src/components/core/common/Table/styles.ts
+++ b/src/components/core/common/Table/styles.ts
@@ -105,8 +105,9 @@ export const ActionButton = styled.button`
}
`;
-export const ClickableRow = styled(TableRow)`
- cursor: pointer;
+export const ClickableRow = styled(TableRow)<{ $cursor?: string }>`
+ cursor: ${(props) => props.$cursor || "pointer"};
+
&:hover {
background: var(--color-pink-50);
}