change the npm to yarn and update the json

This commit is contained in:
Yifei Hu 2024-07-10 11:00:11 +08:00
parent 78738c9282
commit effd1d38d0
19 changed files with 4111 additions and 6 deletions

View file

@ -88,9 +88,9 @@ There are mainly 2 ways of installing Perplexica - With Docker, Without Docker.
1. Install SearXNG and allow `JSON` format in the SearXNG settings. 1. Install SearXNG and allow `JSON` format in the SearXNG settings.
2. Clone the repository and rename the `sample.config.toml` file to `config.toml` in the root directory. Ensure you complete all required fields in this file. 2. Clone the repository and rename the `sample.config.toml` file to `config.toml` in the root directory. Ensure you complete all required fields in this file.
3. Rename the `.env.example` file to `.env` in the `ui` folder and fill in all necessary fields. 3. Rename the `.env.example` file to `.env` in the `ui` folder and fill in all necessary fields.
4. After populating the configuration and environment files, run `npm i` in both the `ui` folder and the root directory. 4. After populating the configuration and environment files, run `yarn install` in both the `ui` folder and the root directory.
5. Install the dependencies and then execute `npm run build` in both the `ui` folder and the root directory. 5. Install the dependencies and then execute `yarn build` in both the `ui` folder and the root directory.
6. Finally, start both the frontend and the backend by running `npm run start` in both the `ui` folder and the root directory. 6. Finally, start both the frontend and the backend by running `yarn start` in both the `ui` folder and the root directory.
**Note**: Using Docker is recommended as it simplifies the setup process, especially for managing environment variables and dependencies. **Note**: Using Docker is recommended as it simplifies the setup process, especially for managing environment variables and dependencies.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

32
public/data/index.json Normal file
View file

@ -0,0 +1,32 @@
[
{
"id": "ed701716-e443-5804-aaa9-99e7e04c33e2",
"title": "胖东来食品安全事件:迅速回应与整改措施",
"summary": "一位顾客在抖音平台曝光了新乡胖东来餐饮部的卫生问题,引发广泛关注。胖东来商贸集团迅速回应,发布调查报告并采取补偿措施,包括现金奖励和退款。公司还对相关责任人进行了处理,并制定了整改措施以提升食品安全标准。尽管事件对品牌形象造成影响,但透明处理赢得了消费者的信任和支持。"
},
{
"id": "869d933c-e186-51b0-a225-edc2d338b6fa",
"title": "姜萍:从中专生到数学竞赛决赛的传奇",
"summary": "来自江苏涟水中等专业学校的17岁女生姜萍以其卓越的数学才能和不懈的努力成功闯入2024年阿里巴巴全球数学竞赛决赛取得第12名的优异成绩。她的成就不仅打破了对中专生和女性在数学领域的刻板印象还引发了关于教育公平的广泛讨论。尽管面临质疑姜萍的故事激励了无数人展示了知识改变命运的力量。"
},
{
"id": "73090b7b-a04e-5c50-ae84-c77f1d7d03a8",
"title": "梅州洪灾:紧急救援与重建希望",
"summary": "近日,梅州遭遇罕见洪灾,暴雨引发的洪水和山体滑坡导致多个乡镇严重受灾,造成重大人员伤亡和经济损失。政府和社会各界迅速展开紧急救援行动,社区成员齐心协力,积极投身于家园重建工作。同时,卫生部门展开大规模防疫行动,确保灾后安全。受灾居民展现出惊人的坚韧与希望,共同努力恢复正常生活。"
},
{
"id": "1fa9b398-080f-54de-9a4f-3ba386acce11",
"title": "东方甄选主播顿顿直播间公开表达不满引发热议",
"summary": "在一次直播中,东方甄选主播顿顿对公司管理表达了强烈不满,指出沟通不畅和公关反应迟缓等问题。这一言论迅速引发广泛关注,不仅影响了公司股价,还引起了粉丝的热烈讨论。市场对公司管理的担忧加剧,凸显了内部管理亟需改进的迫切性。"
},
{
"id": "fd20973f-54b2-5b15-bf7b-79b5e5515b5c",
"title": "鸿蒙智行销量创新高,智能科技引领豪华车新潮流",
"summary": "鸿蒙智行在2024年上半年实现了近20万辆的交付量成为中国新势力品牌的销量冠军。其关键车型如问界M9和新M7表现尤为突出凭借卓越的产品质量和技术创新鸿蒙智行不仅提升了用户的驾驶体验还重新定义了豪华车的标准。未来鸿蒙智行将继续推动行业变革推出更多创新车型和技术巩固其市场领导地位。"
},
{
"id": "b84c7962-2971-53de-a99b-3c9e45492c79",
"title": "Apple Secures Observer Role on OpenAI Board",
"summary": "Apple has taken a significant step in enhancing its AI capabilities by obtaining an observer position on OpenAI's board. This move, highlighted by the appointment of Phil Schiller, aims to deepen Apple's understanding of AI advancements. The integration of ChatGPT into Apple's ecosystem promises to revolutionize user experiences, positioning Apple at the forefront of AI innovation and consumer technology."
}
]

View file

@ -0,0 +1,12 @@
import Link from "next/link";
export default function NewsLayout({ children }: { children: React.ReactNode }) {
return (
<div className="max-w-4xl mx-auto p-4">
<Link href="/" className="text-blue-500 hover:underline mb-4 inline-block">
&larr; Back to News List
</Link>
{children}
</div>
);
}

13
ui/app/news/[id]/page.tsx Normal file
View file

@ -0,0 +1,13 @@
import { fetchNewsData } from "../../../lib/fetchNewsData";
import NewsDetail from "../../../components/NewsDetail";
export default async function NewsPage({ params }: { params: { id: string } }) {
const newsData = await fetchNewsData(params.id);
if (!newsData) {
return <div>News not found</div>;
}
return <NewsDetail news={newsData} />;
}

12
ui/app/news/layout.tsx Normal file
View file

@ -0,0 +1,12 @@
import { Metadata } from "next";
import React from "react";
export const metadata: Metadata = {
title: "News - Perplexica",
};
const Layout = ({ children }: { children: React.ReactNode }) => {
return <div>{children}</div>;
};
export default Layout;

5
ui/app/news/page.tsx Normal file
View file

@ -0,0 +1,5 @@
import NewsPage from "@/components/NewsPage";
export default function Page() {
return <NewsPage />;
}

View file

@ -0,0 +1,42 @@
import React from "react";
import Image from "next/image";
interface ContextItemProps {
item: {
name: string;
url: string;
description: string;
provider: { name: string; image?: { thumbnail: { contentUrl: string } } }[];
datePublished: string;
image?: {
contentUrl: string;
thumbnail: { contentUrl: string; width: number; height: number };
};
};
}
const ContextItem: React.FC<ContextItemProps> = ({ item }) => {
return (
<div className="border p-4 rounded-lg mb-4">
<h4 className="font-bold">{item.name}</h4>
{item.image && (
<Image
src={item.image.contentUrl}
alt={item.name}
width={item.image.thumbnail.width}
height={item.image.thumbnail.height}
className="my-2 rounded"
/>
)}
<p>{item.description}</p>
<div className="text-sm text-gray-500 mt-2">
{item.provider[0].name} | {new Date(item.datePublished).toLocaleDateString()}
</div>
<a href={item.url} target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline">
Read more
</a>
</div>
);
};
export default ContextItem;

View file

@ -4,6 +4,7 @@ const Attach = () => {
return ( return (
<button <button
type="button" type="button"
aria-label="Attach a file"
className="p-2 text-black/50 dark:text-white/50 rounded-xl hover:bg-light-secondary dark:hover:bg-dark-secondary transition duration-200 hover:text-black dark:hover:text-white" className="p-2 text-black/50 dark:text-white/50 rounded-xl hover:bg-light-secondary dark:hover:bg-dark-secondary transition duration-200 hover:text-black dark:hover:text-white"
> >
<CopyPlus /> <CopyPlus />

View file

@ -0,0 +1,35 @@
import React from "react";
import ContextItem from "./ContextItem";
interface NewsDetailProps {
news: {
title: string;
sections: {
title: string;
content: string;
context: any[];
}[];
};
}
const NewsDetail: React.FC<NewsDetailProps> = ({ news }) => {
return (
<article className="prose lg:prose-xl">
<h1>{news.title}</h1>
{news.sections.map((section, index) => (
<section key={index}>
<h2>{section.title}</h2>
<p>{section.content}</p>
<div className="mt-4">
<h3>Related Context:</h3>
{section.context.map((item, i) => (
<ContextItem key={i} item={item} />
))}
</div>
</section>
))}
</article>
);
};
export default NewsDetail;

View file

@ -0,0 +1,85 @@
"use client";
import { useEffect, useState } from "react";
import { Newspaper } from "lucide-react";
import Link from "next/link";
interface NewsItem {
id: string;
title: string;
summary: string;
}
const NewsPage = () => {
const [news, setNews] = useState<NewsItem[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchNews = async () => {
try {
console.log("Fetching news...");
const response = await fetch(
"https://raw.githubusercontent.com/newspedia-crew/newspedia-web/intern-change/public/data/index.json",
);
console.log("Response status:", response.status);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log("Fetched data:", data);
setNews(data);
} catch (error) {
console.error("Error fetching news:", error);
setError(`Failed to load news. Error: ${error instanceof Error ? error.message : String(error)}`);
} finally {
setLoading(false);
}
};
fetchNews();
}, []);
return (
<div>
<div className="fixed z-40 top-0 left-0 right-0 lg:pl-[104px] lg:pr-6 lg:px-8 px-4 py-4 lg:py-6 border-b border-light-200 dark:border-dark-200">
<div className="flex flex-row items-center space-x-2 max-w-screen-lg lg:mx-auto">
<Newspaper />
<h2 className="text-black dark:text-white lg:text-3xl lg:font-medium">News</h2>
</div>
</div>
{loading ? (
<div className="flex flex-row items-center justify-center min-h-screen">
<p className="text-black/70 dark:text-white/70 text-sm">Loading news...</p>
</div>
) : error ? (
<div className="flex flex-col items-center justify-center min-h-screen">
<p className="text-red-500 text-sm mb-2">Failed to load news.</p>
<p className="text-red-500 text-xs">{error}</p>
</div>
) : (
<div className="flex flex-col pt-16 lg:pt-24">
{news.length === 0 ? (
<p className="text-black/70 dark:text-white/70 text-sm text-center">No news available.</p>
) : (
news.map(item => (
<div
key={item.id}
className="flex flex-col space-y-4 border-b border-white-200 dark:border-dark-200 py-6 lg:mx-4"
>
<Link href={`/news/${item.id}`}>
<h3 className="text-black dark:text-white lg:text-xl font-medium hover:underline cursor-pointer">
{item.title}
</h3>
</Link>
<p className="text-black/70 dark:text-white/70 text-sm">{item.summary}</p>
</div>
))
)}
</div>
)}
</div>
);
};
export default NewsPage;

View file

@ -1,7 +1,7 @@
"use client"; "use client";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { BookOpenText, Home, Search, SquarePen, Settings } from "lucide-react"; import { BookOpenText, Home, Search, SquarePen, Settings, Newspaper } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { useSelectedLayoutSegments } from "next/navigation"; import { useSelectedLayoutSegments } from "next/navigation";
import React, { useState, type ReactNode } from "react"; import React, { useState, type ReactNode } from "react";
@ -36,6 +36,12 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => {
active: segments.includes("library"), active: segments.includes("library"),
label: "Library", label: "Library",
}, },
{
icon: Newspaper,
href: "/news",
active: segments.includes("news"),
label: "News",
},
]; ];
return ( return (

9
ui/lib/fetchNewsData.ts Normal file
View file

@ -0,0 +1,9 @@
export async function fetchNewsData(id: string) {
const response = await fetch(
`https://raw.githubusercontent.com/newspedia-crew/newspedia-web/intern-change/public/data/${id}.json`,
);
if (!response.ok) {
return null;
}
return response.json();
}

View file

@ -2,14 +2,20 @@
// eslint-disable-next-line no-undef, @typescript-eslint/no-var-requires // eslint-disable-next-line no-undef, @typescript-eslint/no-var-requires
const webpack = require("webpack"); const webpack = require("webpack");
/** @type {import('next').NextConfig} */ /** @type {import("next").NextConfig} */
const nextConfig = { const nextConfig = {
images: { images: {
remotePatterns: [ remotePatterns: [
{ {
hostname: "s2.googleusercontent.com", protocol: "https",
hostname: "**",
},
{
protocol: "http",
hostname: "**",
}, },
], ],
domains: ["raw.githubusercontent.com"],
}, },
webpack: (config, { isServer }) => { webpack: (config, { isServer }) => {