diff --git a/components/user/rep/MobileIdentityTabContent.tsx b/components/user/rep/MobileIdentityTabContent.tsx
new file mode 100644
index 0000000000..21290f43cb
--- /dev/null
+++ b/components/user/rep/MobileIdentityTabContent.tsx
@@ -0,0 +1,88 @@
+import type { ActivityLogParams } from "@/components/profile-activity/ProfileActivityLogs";
+import type { ApiCicOverview } from "@/generated/models/ApiCicOverview";
+import type { ApiIdentity } from "@/generated/models/ApiIdentity";
+import { formatNumberWithCommas } from "@/helpers/Helpers";
+import { RateMatter } from "@/types/enums";
+import { FingerprintIcon } from "../identity/header/RateNicCta";
+import UserPageIdentityStatementsAddButton from "../identity/statements/add/UserPageIdentityStatementsAddButton";
+import UserPageIdentityStatements from "../identity/statements/UserPageIdentityStatements";
+import UserPageRateWrapper from "../utils/rate/UserPageRateWrapper";
+import UserPageCombinedActivityLog from "./UserPageCombinedActivityLog";
+
+export default function MobileIdentityTabContent({
+ profile,
+ cicOverview,
+ initialActivityLogParams,
+ canEditNic,
+ canEditStatements,
+ onRateNic,
+}: {
+ readonly profile: ApiIdentity;
+ readonly cicOverview: ApiCicOverview | null;
+ readonly initialActivityLogParams: ActivityLogParams;
+ readonly canEditNic: boolean;
+ readonly canEditStatements: boolean;
+ readonly onRateNic: () => void;
+}) {
+ return (
+ <>
+ {canEditNic && (
+
+
+
+
+
+ )}
+
+ {/* Identity Statements */}
+
+
+ ID Statements
+
+ {canEditStatements && (
+
+ )}
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/components/user/rep/MobileRepTabContent.tsx b/components/user/rep/MobileRepTabContent.tsx
new file mode 100644
index 0000000000..fe548f4cea
--- /dev/null
+++ b/components/user/rep/MobileRepTabContent.tsx
@@ -0,0 +1,183 @@
+import type { ActivityLogParams } from "@/components/profile-activity/ProfileActivityLogs";
+import type { ApiRepCategory } from "@/generated/models/ApiRepCategory";
+import type { ApiRepOverview } from "@/generated/models/ApiRepOverview";
+import type { ApiIdentity } from "@/generated/models/ApiIdentity";
+import { formatNumberWithCommas } from "@/helpers/Helpers";
+import { RateMatter } from "@/types/enums";
+import type { RepDirection } from "./UserPageRep.helpers";
+import RepDirectionToggle from "./RepDirectionToggle";
+import RepCategoryPill from "./RepCategoryPill";
+import UserPageCombinedActivityLog from "./UserPageCombinedActivityLog";
+import UserPageRateWrapper from "../utils/rate/UserPageRateWrapper";
+
+function RepEmptyState({
+ loading,
+ repDirection,
+}: {
+ readonly loading: boolean;
+ readonly repDirection: RepDirection;
+}) {
+ if (loading) {
+ return (
+
+ );
+ }
+ return (
+
+ {repDirection === "given" ? "No rep given yet." : "No rep received yet."}
+
+ );
+}
+
+export default function MobileRepTabContent({
+ profile,
+ overview,
+ categories,
+ repDirection,
+ onRepDirectionChange,
+ initialActivityLogParams,
+ loading,
+ canEditRep,
+ visibleCount,
+ onShowMore,
+ onGrantRep,
+ onEditCategory,
+}: {
+ readonly profile: ApiIdentity;
+ readonly overview: ApiRepOverview | null;
+ readonly categories: ApiRepCategory[];
+ readonly repDirection: RepDirection;
+ readonly onRepDirectionChange: (direction: RepDirection) => void;
+ readonly initialActivityLogParams: ActivityLogParams;
+ readonly loading: boolean;
+ readonly canEditRep: boolean;
+ readonly visibleCount: number;
+ readonly onShowMore: () => void;
+ readonly onGrantRep: () => void;
+ readonly onEditCategory: (category: string) => void;
+}) {
+ return (
+ <>
+ {canEditRep && repDirection === "received" && (
+
+
+
+
+
+ )}
+
+ {repDirection === "given" &&
+ overview !== null &&
+ overview.authenticated_user_contribution !== null &&
+ overview.authenticated_user_contribution !== 0 && (
+
+
+ Assigned To You:
+
+
+ {overview.authenticated_user_contribution > 0 && "+"}
+ {formatNumberWithCommas(
+ overview.authenticated_user_contribution
+ )}
+
+
+ )}
+
+
+
+ Rep Categories
+
+
+ {/* Received / Given toggle */}
+
+
+
+
+
+ {categories.length > 0 && (
+
+
+ {categories.slice(0, visibleCount).map((cat) => (
+
+ ))}
+ {categories.length > visibleCount && (
+
+ )}
+
+
+ )}
+
+ {categories.length === 0 && (
+
+ )}
+
+
+
+
+ >
+ );
+}
diff --git a/components/user/rep/MobileTabCards.tsx b/components/user/rep/MobileTabCards.tsx
new file mode 100644
index 0000000000..a98b9b442e
--- /dev/null
+++ b/components/user/rep/MobileTabCards.tsx
@@ -0,0 +1,169 @@
+import { useMemo, type ComponentProps } from "react";
+import type { ApiRepOverview } from "@/generated/models/ApiRepOverview";
+import type { ApiCicOverview } from "@/generated/models/ApiCicOverview";
+import type { ApiIdentity } from "@/generated/models/ApiIdentity";
+import { formatNumberWithCommas } from "@/helpers/Helpers";
+import UserCICStatus from "../utils/user-cic-status/UserCICStatus";
+import UserCICTypeIcon from "../utils/user-cic-type/UserCICTypeIcon";
+import OverlappingAvatars from "@/components/common/OverlappingAvatars";
+import { buildRepAvatarItems } from "./buildRepAvatarItems";
+import { getContributorLabel, type RepDirection } from "./UserPageRep.helpers";
+
+type MobileTab = "rep" | "identity";
+
+export default function MobileTabCards({
+ activeTab,
+ onTabChange,
+ overview,
+ cicOverview,
+ profile,
+ repDirection,
+ cicAvatarItems,
+}: {
+ readonly activeTab: MobileTab;
+ readonly onTabChange: (tab: MobileTab) => void;
+ readonly overview: ApiRepOverview | null;
+ readonly cicOverview: ApiCicOverview | null;
+ readonly profile: ApiIdentity;
+ readonly repDirection: RepDirection;
+ readonly cicAvatarItems: ComponentProps
["items"];
+}) {
+ const repAvatarItems = useMemo(
+ () => buildRepAvatarItems(overview?.contributors.data ?? [], 3),
+ [overview?.contributors.data]
+ );
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/components/user/rep/RepCategoryPill.tsx b/components/user/rep/RepCategoryPill.tsx
index f839428737..78641ed665 100644
--- a/components/user/rep/RepCategoryPill.tsx
+++ b/components/user/rep/RepCategoryPill.tsx
@@ -20,7 +20,10 @@ export default function RepCategoryPill({
readonly compact?: boolean;
readonly direction?: RepDirection;
}) {
- const paddingClass = compact ? "tw-px-3 tw-py-2" : "tw-px-4 tw-py-2.5";
+ const paddingClass = compact ? "tw-px-3 tw-py-2" : "tw-px-4 tw-h-11";
+ const layoutClass = compact
+ ? "tw-inline-flex tw-flex-wrap tw-items-center tw-gap-x-2.5 tw-gap-y-1.5"
+ : "tw-inline-flex tw-items-center tw-gap-2.5";
const avatarItems = useMemo(
() =>
@@ -46,14 +49,14 @@ export default function RepCategoryPill({
const content = (
<>
-
+
{category.category}
{formatNumberWithCommas(category.total_rep)}
- ·
+ ·
{avatarItems.length > 0 && (
- {!!category.authenticated_user_contribution && (
+ {category.authenticated_user_contribution !== null &&
+ category.authenticated_user_contribution !== 0 && (
<>
·
- {direction === "given" ? "To Me:" : "My Rate:"}{" "}
+ {direction === "given"
+ ? "Assigned To You:"
+ : "You Assigned:"}{" "}
+ {category.authenticated_user_contribution > 0 && "+"}
{formatNumberWithCommas(category.authenticated_user_contribution)}
@@ -88,7 +95,7 @@ export default function RepCategoryPill({
>
);
- const baseClasses = `group tw-inline-flex tw-items-center tw-gap-2.5 tw-rounded-lg tw-border tw-border-solid tw-border-white/10 tw-bg-white/5 tw-backdrop-blur-md tw-transition-all tw-duration-300 tw-ease-out ${paddingClass}`;
+ const baseClasses = `group ${layoutClass} tw-rounded-lg tw-border tw-border-solid tw-border-white/10 tw-bg-[#18191B] tw-transition-all tw-duration-300 tw-ease-out ${paddingClass}`;
if (canEdit) {
return (
diff --git a/components/user/rep/RepDirectionToggle.tsx b/components/user/rep/RepDirectionToggle.tsx
new file mode 100644
index 0000000000..af470505c1
--- /dev/null
+++ b/components/user/rep/RepDirectionToggle.tsx
@@ -0,0 +1,49 @@
+import { ArrowDownLeftIcon, ArrowUpRightIcon } from "@heroicons/react/24/solid";
+import type { RepDirection } from "./UserPageRep.helpers";
+
+export default function RepDirectionToggle({
+ repDirection,
+ onRepDirectionChange,
+ compact,
+}: {
+ readonly repDirection: RepDirection;
+ readonly onRepDirectionChange: (direction: RepDirection) => void;
+ readonly compact?: boolean;
+}) {
+ const iconClass = compact
+ ? "tw-h-3 tw-w-3 tw-flex-shrink-0"
+ : "tw-h-3.5 tw-w-3.5 tw-flex-shrink-0";
+ const textClass = compact ? "tw-text-xs" : "tw-text-[13px]";
+ const activeExtra = compact ? "tw-font-semibold" : "";
+
+ return (
+
+
+
+
+ );
+}
diff --git a/components/user/rep/UserPageCombinedActivityLog.tsx b/components/user/rep/UserPageCombinedActivityLog.tsx
index bc2f7243f7..ad39922afa 100644
--- a/components/user/rep/UserPageCombinedActivityLog.tsx
+++ b/components/user/rep/UserPageCombinedActivityLog.tsx
@@ -23,7 +23,7 @@ export default function UserPageCombinedActivityLog({
withFilters={true}
withMatterFilter={withMatterFilter}
>
-
+