mirror of
https://github.com/hpware/news-analyze.git
synced 2025-06-23 15:51:01 +08:00
It's so bad that it works... AI: feat: update environment variables in .env.example; enhance chatbot and hotnews components; improve sources layout and image handling; add new logo assets
This commit is contained in:
parent
34a0868b26
commit
5d77b2770d
25
.env.example
25
.env.example
@ -3,15 +3,24 @@
|
|||||||
# If you are using Windows, run ps1 clone-env.ps1 in the terminal.
|
# If you are using Windows, run ps1 clone-env.ps1 in the terminal.
|
||||||
|
|
||||||
# This is the default .env file.
|
# This is the default .env file.
|
||||||
S3_ACCESS_KEY=
|
|
||||||
S3_SECRET_KEY=
|
|
||||||
S3_BUCKETNAME=
|
|
||||||
S3_ENDPOINT=
|
|
||||||
|
|
||||||
POSTGRES_URL=
|
# S3 INFO
|
||||||
|
S3_ACCESS_KEY=""
|
||||||
|
S3_SECRET_KEY=""
|
||||||
|
S3_BUCKETNAME=""
|
||||||
|
S3_ENDPOINT=""
|
||||||
|
|
||||||
GROQ_API_KEY=
|
# GITHUB OAUTH (NOT WORKING 4n)
|
||||||
|
NUXT_GITHUB_CLIENT_ID=""
|
||||||
|
NUXT_GITHUB_CLIENT_SECRET=""
|
||||||
|
|
||||||
|
# GLOBAL DATABASE
|
||||||
|
POSTGRES_URL=""
|
||||||
|
|
||||||
|
# GROQ API KEY
|
||||||
|
GROQ_API_KEY=""
|
||||||
|
|
||||||
|
# PASSWORD SALT
|
||||||
PASSWORD_HASH_SALT=""
|
PASSWORD_HASH_SALT=""
|
||||||
|
|
||||||
# SCRAPING
|
# SCRAPING
|
||||||
@ -20,3 +29,7 @@ POSTGRES_USER=""
|
|||||||
POSTGRES_PASSWORD=""
|
POSTGRES_PASSWORD=""
|
||||||
POSTGRES_HOST=""
|
POSTGRES_HOST=""
|
||||||
POSTGRES_PORT=""
|
POSTGRES_PORT=""
|
||||||
|
|
||||||
|
# CF TURNSTILE
|
||||||
|
NUXT_CF_TURNSTILE_SITE_KEY=""
|
||||||
|
NUXT_CF_TURNSTILE_SECRET_KEY=""
|
||||||
|
@ -12,18 +12,20 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="justify-center align-center text-center flex flex-col">
|
<div class="justify-center align-center text-center flex flex-col">
|
||||||
<div class="flex flex-row justify-between">
|
<div class="flex flex-row justify-between">
|
||||||
<div>Chat Name</div>
|
<div>Chat Name</div>
|
||||||
<div class="flex flex-row justify-center align-center text-center">
|
<div class="flex flex-row justify-center align-center text-center">
|
||||||
<div class="flex flex-row justify-center align-center text-center gap-2">
|
<div
|
||||||
<button class="p-2 rounded-lg hover:bg-gray-300">
|
class="flex flex-row justify-center align-center text-center gap-2"
|
||||||
<History class="h-4 w-4" />
|
>
|
||||||
</button>
|
<button class="p-2 rounded-lg hover:bg-gray-300">
|
||||||
<button class="p-2 rounded-lg hover:bg-gray-300">
|
<History class="h-4 w-4" />
|
||||||
<Plus class="h-4 w-4" />
|
</button>
|
||||||
</button>
|
<button class="p-2 rounded-lg hover:bg-gray-300">
|
||||||
|
<Plus class="h-4 w-4" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -13,9 +13,7 @@ try {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div v-if="!ffeed">
|
<div v-if="!ffeed">Loading...</div>
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-for="item in ffeed"
|
v-for="item in ffeed"
|
||||||
class="justify-center align-center text-center p-4 border border-black rounded-lg m-4"
|
class="justify-center align-center text-center p-4 border border-black rounded-lg m-4"
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import noImageLogo from "~/public/geterrorassets/noImageLogo.svg";
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
|
|
||||||
|
const emit = defineEmits(['windowopener'])
|
||||||
|
|
||||||
|
const openNewWindow = (windowName: string) => {
|
||||||
|
emit('windowopener', windowName)
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: source,
|
data: source,
|
||||||
pending,
|
pending,
|
||||||
@ -14,13 +21,52 @@ const {
|
|||||||
lang: locale,
|
lang: locale,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function getImageSource(image: string) {
|
||||||
|
console.log(image);
|
||||||
|
if (!image || image === "#") {
|
||||||
|
return noImageLogo;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const checkImgWorks = await fetch(image, {
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
if (checkImgWorks.status === 200) {
|
||||||
|
return image;
|
||||||
|
} else {
|
||||||
|
return noImageLogo;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return noImageLogo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const imageUrls = ref<{ [key: string]: string }>({});
|
||||||
|
|
||||||
|
// Load image sources when component mounts
|
||||||
|
onMounted(async () => {
|
||||||
|
if (source.value?.data) {
|
||||||
|
for (const item of source.value.data) {
|
||||||
|
imageUrls.value[item.id] = await getImageSource(item.logo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="flex flex-row flexw-wrap justify-center gap-2">
|
||||||
<div v-for="item in source?.data" :key="item.id">
|
<div
|
||||||
<h1>{{ item.title }}</h1>
|
class="flex flex-col group bg-gray-900/30 rounded-xl p-3 transition-all duration-500 shadow-lg hover:translate-y-[-2px] ransition-all duration-700"
|
||||||
<span>{{ item.description }}</span>
|
v-for="item in source?.data"
|
||||||
<a :href="item.url">{{ item.url }}</a>
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<button @click="openNewWindow('chatbot')">
|
||||||
|
<img
|
||||||
|
:src="imageUrls[item.id] || noImageLogo"
|
||||||
|
alt="Organization Logo"
|
||||||
|
class="w-16 h-16 rounded-xl"
|
||||||
|
/>
|
||||||
|
<h1>{{ item.title }}</h1>
|
||||||
|
<span>{{ item.description }}</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
15
layouts/docs.vue
Normal file
15
layouts/docs.vue
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Navigation from "~/components/navigation.vue";
|
||||||
|
import Footer from "~/components/footer.vue";
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<nav>
|
||||||
|
<Navigation />
|
||||||
|
</nav>
|
||||||
|
<main class="pt-[60px]">
|
||||||
|
<slot />
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<Footer />
|
||||||
|
</footer>
|
||||||
|
</template>
|
@ -66,6 +66,7 @@ const openAppNameQuery = ref();
|
|||||||
const currentOpenAppId = ref(0);
|
const currentOpenAppId = ref(0);
|
||||||
const progress = ref(0);
|
const progress = ref(0);
|
||||||
const titleAppName = ref("Desktop");
|
const titleAppName = ref("Desktop");
|
||||||
|
const openingAppViaAnApp = ref(false);
|
||||||
|
|
||||||
// Key Data
|
// Key Data
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
@ -100,6 +101,8 @@ const associAppWindow = [
|
|||||||
id: "3",
|
id: "3",
|
||||||
title: t("app.sources"),
|
title: t("app.sources"),
|
||||||
component: SourcesWindow,
|
component: SourcesWindow,
|
||||||
|
width: "700px",
|
||||||
|
height: "500px",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "about",
|
name: "about",
|
||||||
@ -236,14 +239,16 @@ const findAndOpenWindow = (windowName: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const obtainTopWindowPosition = (windowId: string) => {
|
const obtainTopWindowPosition = (windowId: string) => {
|
||||||
const windowIndex = activeWindows.value.findIndex(
|
if (!openingAppViaAnApp.value) {
|
||||||
|
const windowIndex = activeWindows.value.findIndex(
|
||||||
(window) => window.id === windowId,
|
(window) => window.id === windowId,
|
||||||
);
|
);
|
||||||
console.log(windowIndex);
|
|
||||||
if (windowIndex !== -1) {
|
if (windowIndex !== -1) {
|
||||||
const [window] = activeWindows.value.splice(windowIndex, 1);
|
const [window] = activeWindows.value.splice(windowIndex, 1);
|
||||||
|
titleAppName.value = window.name;
|
||||||
activeWindows.value.push(window);
|
activeWindows.value.push(window);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeWindow = (windowId: string) => {
|
const closeWindow = (windowId: string) => {
|
||||||
@ -253,6 +258,14 @@ const closeWindow = (windowId: string) => {
|
|||||||
console.log("activeWindows.value", activeWindows.value);
|
console.log("activeWindows.value", activeWindows.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openNewWindowViaApp = (windowId: string) => {
|
||||||
|
openingAppViaAnApp.value = true;
|
||||||
|
findAndOpenWindow(windowId);
|
||||||
|
setTimeout(() => {
|
||||||
|
openingAppViaAnApp.value = false;
|
||||||
|
},1000);
|
||||||
|
}
|
||||||
|
|
||||||
const maxWindow = (windowId: string) => {};
|
const maxWindow = (windowId: string) => {};
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
@ -416,6 +429,7 @@ watchEffect((cleanupFn) => {
|
|||||||
<Component
|
<Component
|
||||||
:is="window.component"
|
:is="window.component"
|
||||||
@error="console.error('Error:', $event)"
|
@error="console.error('Error:', $event)"
|
||||||
|
@windowopener="openNewWindowViaApp($event)"
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DraggableWindow>
|
</DraggableWindow>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-[200px]"></div>
|
<div class="h-[200px]"></div>
|
||||||
<h1 class="text-4xl text-center align-middle">Documentation is currently not available.</h1>
|
<h1 class="text-4xl text-center align-middle">
|
||||||
|
Documentation is currently not available.
|
||||||
|
</h1>
|
||||||
</template>
|
</template>
|
@ -5,7 +5,13 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "~/components/ui/accordion";
|
} from "~/components/ui/accordion";
|
||||||
import { ComputerDesktopIcon, CircleStackIcon, MagnifyingGlassIcon, ViewfinderCircleIcon, DocumentDuplicateIcon } from "@heroicons/vue/24/outline";
|
import {
|
||||||
|
ComputerDesktopIcon,
|
||||||
|
CircleStackIcon,
|
||||||
|
MagnifyingGlassIcon,
|
||||||
|
ViewfinderCircleIcon,
|
||||||
|
DocumentDuplicateIcon,
|
||||||
|
} from "@heroicons/vue/24/outline";
|
||||||
import { gsap } from "gsap";
|
import { gsap } from "gsap";
|
||||||
import { TextPlugin } from "gsap/TextPlugin";
|
import { TextPlugin } from "gsap/TextPlugin";
|
||||||
gsap.registerPlugin(TextPlugin);
|
gsap.registerPlugin(TextPlugin);
|
||||||
@ -26,19 +32,19 @@ const cards = [
|
|||||||
{
|
{
|
||||||
icon: ViewfinderCircleIcon,
|
icon: ViewfinderCircleIcon,
|
||||||
title: t("home.cards.title.find"),
|
title: t("home.cards.title.find"),
|
||||||
description: t("home.cards.description.find"),
|
description: t("home.cards.description.find"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ComputerDesktopIcon,
|
icon: ComputerDesktopIcon,
|
||||||
title: t("home.cards.title.interface"),
|
title: t("home.cards.title.interface"),
|
||||||
description: t("home.cards.description.interface"),
|
description: t("home.cards.description.interface"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: DocumentDuplicateIcon,
|
icon: DocumentDuplicateIcon,
|
||||||
title: t("home.cards.title.documentation"),
|
title: t("home.cards.title.documentation"),
|
||||||
description: t("home.cards.description.documentation"),
|
description: t("home.cards.description.documentation"),
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
const accordionItems = [
|
const accordionItems = [
|
||||||
{
|
{
|
||||||
@ -133,18 +139,31 @@ onMounted(() => {
|
|||||||
<div class="h-screen"></div>
|
<div class="h-screen"></div>
|
||||||
<div id="learnmore"></div>
|
<div id="learnmore"></div>
|
||||||
<div class="h-[100px]"></div>
|
<div class="h-[100px]"></div>
|
||||||
<div class="">
|
<div
|
||||||
<div class="justify-center align-center gap-2 p-2 w-full flex flex-row flex-wrap" id="cards">
|
class="justify-center align-center gap-2 p-2 w-full flex flex-row flex-wrap relative"
|
||||||
<div class="px-10 bg-gray-900/70 w-[300px] h-[200px] rounded-xl shadow-lg hover:shadow-sky-600/90 backdrop-blur-sm border border-gray-800 hover:border-gray-600/70 transition-all duration-700 justify-center align-middle flex flex-col" v-for="item in cards" :key="item.title">
|
id="cards"
|
||||||
<component :is="item.icon" class="w-8 h-8" />
|
>
|
||||||
<h3 class="text-xl font-bold">{{ item.title }}</h3>
|
<div
|
||||||
<p class="">{{ item.description }}</p>
|
class="px-10 bg-gray-900/70 w-[300px] h-[200px] group rounded-xl shadow-lg hover:shadow-sky-600/90 backdrop-blur-sm border border-gray-800 hover:border-gray-600/70 transition-all duration-700 justify-center align-middle flex flex-col"
|
||||||
|
v-for="item in cards"
|
||||||
|
:key="item.title"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="item.icon"
|
||||||
|
class="w-8 h-8 text-white group-hover:text-sky-500 transition-colors duration-300"
|
||||||
|
/>
|
||||||
|
<h3 class="text-xl font-bold">{{ item.title }}</h3>
|
||||||
|
<p
|
||||||
|
class="text-gray-400 group-hover:text-gray-300 transition-colors duration-300"
|
||||||
|
>
|
||||||
|
{{ item.description }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="justify-center align-center text-center w-full flex">
|
<div class="justify-center align-center text-center w-full flex">
|
||||||
<div
|
<div
|
||||||
class="bg-gray-900/60 rounded-xl shadow-lg p-8 border border-gray-800 hover:border-sky-700 m-4 w-1/2 align-center justify-center text-center duration-700"
|
class="bg-gray-900/60 rounded-xl shadow-lg p-8 border border-gray-800 hover:border-sky-700 m-4 w-full md:w-1/2 align-center justify-center text-center duration-700"
|
||||||
>
|
>
|
||||||
<span class="text-2xl font-bold">Q/A</span>
|
<span class="text-2xl font-bold">Q/A</span>
|
||||||
<Accordion
|
<Accordion
|
||||||
@ -157,7 +176,9 @@ onMounted(() => {
|
|||||||
:key="item.value"
|
:key="item.value"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
>
|
>
|
||||||
<AccordionTrigger>{{ item.title }}</AccordionTrigger>
|
<AccordionTrigger class="transition-all duration-100">{{
|
||||||
|
item.title
|
||||||
|
}}</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
{{ item.content }}
|
{{ item.content }}
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
@ -166,5 +187,4 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
6
public/geterrorassets/noImageLogo.svg
Normal file
6
public/geterrorassets/noImageLogo.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg width='16' height='16' xmlns='http://www.w3.org/2000/svg'>
|
||||||
|
<rect width='100%' height='100%' fill='#333'/>
|
||||||
|
<text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' fill='#666' font-size='4'>
|
||||||
|
無圖片
|
||||||
|
</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 252 B |
3
public/logos/GitHub-dark.svg
Normal file
3
public/logos/GitHub-dark.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" transform="scale(64)" fill="#ffff"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 966 B |
@ -11,12 +11,14 @@ export default defineEventHandler(async (event) => {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: "Source 1",
|
title: "Source 1",
|
||||||
|
logo: "#",
|
||||||
url: "https://source1.com",
|
url: "https://source1.com",
|
||||||
description: "Description for Source 1",
|
description: "Description for Source 1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
title: "Source 2",
|
title: "Source 2",
|
||||||
|
logo: "#",
|
||||||
url: "https://source2.com",
|
url: "https://source2.com",
|
||||||
description: "Description for Source 2",
|
description: "Description for Source 2",
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user