This commit is contained in:
root
2026-06-07 18:06:52 +08:00
parent 9de2465793
commit a1daa040ef
33 changed files with 1917 additions and 439 deletions

View File

@@ -1,6 +1,5 @@
"use client";
import { useState } from "react";
import { Link, useTranslation } from "@/app/digital/i18n/navigation";
import type { ResourceData } from "@/app/digital/lib/theme-data";
import { useCrossSiteUrls } from "@/app/digital/lib/useCrossSiteUrls";
@@ -30,13 +29,19 @@ const linkIconNames: Record<string, IconName> = {
unmannedLive: "video",
};
const topicHrefs: Record<(typeof topicKeys)[number], string> = {
indieDev: "/ebook",
aiAgent: "/#tools",
cloudPhone: "/ebook",
unmannedLive: "/ebook",
};
type CommunityProps = {
resourceData?: ResourceData;
};
export default function Community({ resourceData }: CommunityProps) {
const { t } = useTranslation("community");
const [toast, setToast] = useState(false);
const { meetupUrl, vipUrl } = useCrossSiteUrls();
const linkHrefs: Record<string, { href: string; newTab?: boolean }> = {
destinations: { href: meetupUrl, newTab: true },
@@ -44,11 +49,6 @@ export default function Community({ resourceData }: CommunityProps) {
appDownload: { href: "/app" },
};
const showUpdating = () => {
setToast(true);
setTimeout(() => setToast(false), 2000);
};
const renderCardContent = (key: string) => (
<>
<span className="dn-icon-box flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-slate-50 text-xl dark:bg-slate-800">
@@ -101,18 +101,17 @@ export default function Community({ resourceData }: CommunityProps) {
})}
</div>
{/* 4 个专题 - 小卡片,暂不可点击 */}
{/* 4 个专题 */}
<div className="mt-8">
<h3 className="mb-4 text-center text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400">
{t("topicTag")}
</h3>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
{topicKeys.map((key) => (
<button
<Link
key={key}
type="button"
onClick={showUpdating}
className="dn-card dn-muted-card flex cursor-not-allowed items-start gap-4 rounded-xl border border-slate-200 bg-slate-50/80 p-4 opacity-90 transition-opacity hover:opacity-100 dark:border-slate-700 dark:bg-slate-800/60"
href={topicHrefs[key]}
className="dn-card dn-muted-card flex items-start gap-4 rounded-xl border border-slate-200 bg-slate-50/80 p-4 opacity-90 transition-all hover:opacity-100 dark:border-slate-700 dark:bg-slate-800/60"
>
<span className="dn-icon-box flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-slate-200 text-lg dark:bg-slate-700">
<DigitalThemeIcon
@@ -124,24 +123,14 @@ export default function Community({ resourceData }: CommunityProps) {
<div className="min-w-0 flex-1 text-left">
<h4 className="font-bold text-slate-700 dark:text-slate-300">{t(`links.${key}.title`)}</h4>
<p className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">{t(`links.${key}.desc`)}</p>
<span className="mt-1.5 inline-block text-xs font-medium text-amber-600 dark:text-amber-400">
{t("updating")}
<span className="dn-text-link mt-1.5 inline-block text-xs font-medium text-sky-600">
{t(`links.${key}.label`)}
</span>
</div>
</button>
</Link>
))}
</div>
</div>
{/* 更新中 Toast */}
{toast && (
<div
className="fixed bottom-8 left-1/2 z-50 -translate-x-1/2 rounded-lg bg-slate-800 px-6 py-3 text-sm font-medium text-white shadow-lg dark:bg-slate-700"
role="alert"
>
{t("updating")}
</div>
)}
</div>
</section>
);

View File

@@ -19,18 +19,18 @@ export default function Footer() {
{
titleKey: "resources" as const,
links: [
{ labelKey: "remoteJobs" as const, href: "#" },
{ labelKey: "visa" as const, href: "#" },
{ labelKey: "coliving" as const, href: "#" },
{ labelKey: "insurance" as const, href: "#" },
{ labelKey: "remoteJobs" as const, href: "/jobs", internal: true },
{ labelKey: "visa" as const, href: "/services", internal: true },
{ labelKey: "coliving" as const, href: "/services", internal: true },
{ labelKey: "insurance" as const, href: "/services", internal: true },
],
},
{
titleKey: "community" as const,
links: [
{ labelKey: "discord" as const, href: "#" },
{ labelKey: "discord" as const, href: "/join", internal: true },
{ labelKey: "wechat" as const, href: vipUrl, openInNewTab: true },
{ labelKey: "jike" as const, href: "#" },
{ labelKey: "jike" as const, href: "/discuss", internal: true },
],
},
{
@@ -39,7 +39,7 @@ export default function Footer() {
{ labelKey: "aboutUs" as const, href: "/about", internal: true },
{ labelKey: "recruit" as const, href: "/jobs", internal: true },
{ labelKey: "privacy" as const, href: "/privacy", internal: true, openInNewTab: true },
{ labelKey: "contact" as const, href: "#" },
{ labelKey: "contact" as const, href: "/contact", internal: true },
],
},
];

View File

@@ -49,12 +49,6 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
const [activePanCategory, setActivePanCategory] = useState(firstCategoryId);
const [booksPage, setBooksPage] = useState(0);
const [productsPage, setProductsPage] = useState(0);
const [toast, setToast] = useState(false);
const showUpdating = () => {
setToast(true);
setTimeout(() => setToast(false), 2000);
};
const activeCategory = data.pan.categories.find((c) => c.id === activePanCategory);
@@ -92,11 +86,12 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
className="grid grid-cols-2 gap-3 gap-y-4 sm:gap-4 md:grid-cols-8"
>
{paginatedBooks.map((book, i) => (
<button
<a
key={`book-${booksPage}-${i}`}
type="button"
onClick={showUpdating}
className="dn-resource-tile group flex cursor-not-allowed flex-col text-left"
href={book.href}
target="_blank"
rel="noopener noreferrer sponsored"
className="dn-resource-tile group flex cursor-pointer flex-col text-left focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-slate-50 dark:focus:ring-offset-slate-950"
>
<div
className="overflow-hidden rounded-lg border border-slate-200 bg-slate-100 shadow-sm transition-all group-hover:shadow-md dark:border-slate-700 dark:bg-slate-800"
@@ -111,7 +106,7 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
<p className="mt-2 line-clamp-2 text-center text-xs font-medium text-slate-700 dark:text-slate-300 sm:text-sm">
{book.title}
</p>
</button>
</a>
))}
</div>
{booksTotalPages > 1 && (
@@ -160,11 +155,12 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
className="grid grid-cols-2 gap-3 gap-y-4 sm:gap-4 md:grid-cols-8"
>
{paginatedProducts.map((product, i) => (
<button
<a
key={`product-${productsPage}-${i}`}
type="button"
onClick={showUpdating}
className="dn-resource-tile group flex cursor-not-allowed flex-col text-left"
href={product.href}
target="_blank"
rel="noopener noreferrer sponsored"
className="dn-resource-tile group flex cursor-pointer flex-col text-left focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-slate-50 dark:focus:ring-offset-slate-950"
>
<div
className="overflow-hidden rounded-xl border border-slate-200 bg-white p-3 shadow-sm transition-all group-hover:shadow-md dark:border-slate-700 dark:bg-slate-900"
@@ -179,7 +175,7 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
<p className="mt-2 line-clamp-2 text-center text-xs font-medium text-slate-700 dark:text-slate-300 sm:text-sm">
{product.name}
</p>
</button>
</a>
))}
</div>
{productsTotalPages > 1 && (
@@ -277,14 +273,6 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
</div>
</div>
{toast && (
<div
className="fixed bottom-8 left-1/2 z-50 -translate-x-1/2 rounded-lg bg-slate-800 px-6 py-3 text-sm font-medium text-white shadow-lg dark:bg-slate-700"
role="alert"
>
{t("updating")}
</div>
)}
</div>
);
}

View File

@@ -3,9 +3,9 @@
* 与 join 报名支付分离,课程解锁用 VIP 状态
*/
/** 前 2 章试看(暂时关闭,全部需 VIP 解锁 */
export function isFreeTrial(_moduleIndex: number, _lessonIndex: number): boolean {
return false;
/** 第一模块前 2 节可试看,其余章节需 VIP 解锁 */
export function isFreeTrial(moduleIndex: number, lessonIndex: number): boolean {
return moduleIndex === 0 && lessonIndex < 2;
}
/** 章节是否可观看(需传入 vip 状态,来自 useAuthAndVip 或 /api/digital/vip/check */