Skip to content

Commit

Permalink
Merge pull request #1188 from prathamesh424/main
Browse files Browse the repository at this point in the history
Reusable  Attractive  Skeleton  Loader Component is  added  [Fixes #1181]
  • Loading branch information
dartpain authored Oct 15, 2024
2 parents 42185a0 + a25d5d9 commit 9be3043
Show file tree
Hide file tree
Showing 11 changed files with 437 additions and 226 deletions.
2 changes: 1 addition & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

138 changes: 138 additions & 0 deletions frontend/src/components/SkeletonLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React, { useState, useEffect } from 'react';

interface SkeletonLoaderProps {
count?: number;
component?: 'default' | 'analysis' | 'chatbot' | 'logs';
}

const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
count = 1,
component = 'default',
}) => {
const [skeletonCount, setSkeletonCount] = useState(count);

useEffect(() => {
const handleResize = () => {
const windowWidth = window.innerWidth;

if (windowWidth > 1024) {
setSkeletonCount(1);
} else if (windowWidth > 768) {
setSkeletonCount(count);
} else {
setSkeletonCount(Math.min(count, 2));
}
};

handleResize();
window.addEventListener('resize', handleResize);

return () => {
window.removeEventListener('resize', handleResize);
};
}, [count]);

return (
<div className="flex flex-col space-y-4">
{component === 'default' ? (
[...Array(skeletonCount)].map((_, idx) => (
<div
key={idx}
className={`p-6 ${skeletonCount === 1 ? 'w-full' : 'w-60'} dark:bg-raisin-black rounded-3xl animate-pulse`}
>
<div className="space-y-4">
<div>
<div className="h-4 bg-gray-600 rounded mb-2 w-3/4"></div>
<div className="h-4 bg-gray-600 rounded mb-2 w-5/6"></div>
<div className="h-4 bg-gray-600 rounded mb-2 w-1/2"></div>
<div className="h-4 bg-gray-600 rounded mb-2 w-3/4"></div>
<div className="h-4 bg-gray-600 rounded mb-2 w-full"></div>
</div>
<div className="border-t border-gray-600 my-4"></div>
<div>
<div className="h-4 bg-gray-600 rounded mb-2 w-2/3"></div>
<div className="h-4 bg-gray-600 rounded mb-2 w-1/4"></div>
<div className="h-4 bg-gray-600 rounded mb-2 w-full"></div>
</div>
<div className="border-t border-gray-600 my-4"></div>
<div>
<div className="h-4 bg-gray-600 rounded mb-2 w-5/6"></div>
<div className="h-4 bg-gray-600 rounded mb-2 w-1/3"></div>
<div className="h-4 bg-gray-600 rounded mb-2 w-2/3"></div>
<div className="h-4 bg-gray-600 rounded mb-2 w-full"></div>
</div>
<div className="border-t border-gray-600 my-4"></div>
<div className="h-4 bg-gray-600 rounded w-3/4 mb-2"></div>
<div className="h-4 bg-gray-600 rounded w-5/6 mb-2"></div>
</div>
</div>
))
) : component === 'analysis' ? (
[...Array(skeletonCount)].map((_, idx) => (
<div
key={idx}
className="p-6 w-full dark:bg-raisin-black rounded-3xl animate-pulse"
>
<div className="space-y-6">
<div className="space-y-2">
<div className="h-4 bg-gray-600 rounded w-1/3 mb-4"></div>
<div className="grid grid-cols-6 gap-2 items-end">
<div className="h-32 bg-gray-600 rounded"></div>
<div className="h-24 bg-gray-600 rounded"></div>
<div className="h-40 bg-gray-600 rounded"></div>
<div className="h-28 bg-gray-600 rounded"></div>
<div className="h-36 bg-gray-600 rounded"></div>
<div className="h-20 bg-gray-600 rounded"></div>
</div>
</div>
<div className="space-y-2">
<div className="h-4 bg-gray-600 rounded w-1/4 mb-4"></div>
<div className="h-32 bg-gray-600 rounded"></div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="h-4 bg-gray-600 rounded w-full"></div>
<div className="h-4 bg-gray-600 rounded w-full"></div>
</div>
</div>
</div>
))
) : component === 'chatbot' ? (
<div className="space-y-2 p-6 w-full dark:bg-raisin-black rounded-3xl animate-pulse">
<div className="grid grid-cols-4 gap-2 p-2">
<div className="h-4 bg-gray-600 rounded w-full"></div>
<div className="h-4 bg-gray-600 rounded w-full"></div>
<div className="h-4 bg-gray-600 rounded w-full"></div>
<div className="h-4 bg-gray-600 rounded w-full"></div>
</div>
<div className="border-t border-gray-600 my-2"></div>

{[...Array(skeletonCount * 6)].map((_, idx) => (
<div key={idx} className="grid grid-cols-4 gap-2 p-2 space-x-2">
<div className="h-4 bg-gray-500 rounded w-full"></div>
<div className="h-4 bg-gray-500 rounded w-full"></div>
<div className="h-4 bg-gray-500 rounded w-full"></div>
<div className="h-4 bg-gray-500 rounded w-full"></div>
</div>
))}
</div>
) : (
[...Array(skeletonCount)].map((_, idx) => (
<div
key={idx}
className="p-6 w-full dark:bg-raisin-black rounded-3xl animate-pulse"
>
<div className="space-y-4">
<div className="h-4 bg-gray-600 rounded w-1/2"></div>
<div className="h-4 bg-gray-600 rounded w-5/6"></div>
<div className="h-4 bg-gray-600 rounded w-3/4"></div>
<div className="h-4 bg-gray-600 rounded w-2/3"></div>
<div className="h-4 bg-gray-600 rounded w-1/4"></div>
</div>
</div>
))
)}
</div>
);
};

export default SkeletonLoader;
6 changes: 3 additions & 3 deletions frontend/src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"sourceDocs": "Source",
"none": "None",
"cancel": "Cancel",
"help":"Help",
"emailUs":"Email us",
"documentation":"documentation",
"help": "Help",
"emailUs": "Email us",
"documentation": "documentation",
"demo": [
{
"header": "Learn about DocsGPT",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/locale/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"sourceDocs": "Fuente",
"none": "Nada",
"cancel": "Cancelar",
"help":"Asistencia",
"help": "Asistencia",
"emailUs": "Envíanos un correo",
"documentation": "documentación",
"demo": [
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/locale/jp.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"sourceDocs": "ソース",
"none": "なし",
"cancel": "キャンセル",
"help":"ヘルプ",
"help": "ヘルプ",
"emailUs": "メールを送る",
"documentation": "ドキュメント",
"demo": [
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/locale/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"sourceDocs": "原始文件",
"none": "",
"cancel": "取消",
"help":"聯繫支援",
"help": "聯繫支援",
"emailUs": "寄送電子郵件給我們",
"documentation": "文件",
"demo": [
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/locale/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"sourceDocs": "",
"none": "",
"cancel": "取消",
"help":"联系支持",
"help": "联系支持",
"emailUs": "给我们发邮件",
"documentation": "文档",
"demo": [
Expand Down
78 changes: 44 additions & 34 deletions frontend/src/settings/APIKeys.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import React from 'react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';

import userService from '../api/services/userService';
import Trash from '../assets/trash.svg';
import CreateAPIKeyModal from '../modals/CreateAPIKeyModal';
import SaveAPIKeyModal from '../modals/SaveAPIKeyModal';
import { APIKeyData } from './types';
import SkeletonLoader from '../components/SkeletonLoader';

export default function APIKeys() {
const { t } = useTranslation();
const [isCreateModalOpen, setCreateModal] = React.useState(false);
const [isSaveKeyModalOpen, setSaveKeyModal] = React.useState(false);
const [newKey, setNewKey] = React.useState('');
const [apiKeys, setApiKeys] = React.useState<APIKeyData[]>([]);
const [loading, setLoading] = useState(true);

const handleFetchKeys = async () => {
setLoading(true);
try {
const response = await userService.getAPIKeys();
if (!response.ok) {
Expand All @@ -24,6 +27,8 @@ export default function APIKeys() {
setApiKeys(apiKeys);
} catch (error) {
console.log(error);
} finally {
setLoading(false);
}
};

Expand Down Expand Up @@ -75,6 +80,7 @@ export default function APIKeys() {
React.useEffect(() => {
handleFetchKeys();
}, []);

return (
<div className="mt-8">
<div className="flex flex-col max-w-[876px]">
Expand All @@ -100,41 +106,45 @@ export default function APIKeys() {
)}
<div className="mt-[27px] w-full">
<div className="w-full overflow-x-auto">
<table className="table-default">
<thead>
<tr>
<th>{t('settings.apiKeys.name')}</th>
<th>{t('settings.apiKeys.sourceDoc')}</th>
<th>{t('settings.apiKeys.key')}</th>
<th></th>
</tr>
</thead>
<tbody>
{!apiKeys?.length && (
{loading ? (
<SkeletonLoader count={1} component={'chatbot'} />
) : (
<table className="table-default">
<thead>
<tr>
<td colSpan={4} className="!p-4">
{t('settings.apiKeys.noData')}
</td>
</tr>
)}
{apiKeys?.map((element, index) => (
<tr key={index}>
<td>{element.name}</td>
<td>{element.source}</td>
<td>{element.key}</td>
<td>
<img
src={Trash}
alt="Delete"
className="h-4 w-4 cursor-pointer hover:opacity-50"
id={`img-${index}`}
onClick={() => handleDeleteKey(element.id)}
/>
</td>
<th>{t('settings.apiKeys.name')}</th>
<th>{t('settings.apiKeys.sourceDoc')}</th>
<th>{t('settings.apiKeys.key')}</th>
<th></th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{!apiKeys?.length && (
<tr>
<td colSpan={4} className="!p-4">
{t('settings.apiKeys.noData')}
</td>
</tr>
)}
{apiKeys?.map((element, index) => (
<tr key={index}>
<td>{element.name}</td>
<td>{element.source}</td>
<td>{element.key}</td>
<td>
<img
src={Trash}
alt="Delete"
className="h-4 w-4 cursor-pointer hover:opacity-50"
id={`img-${index}`}
onClick={() => handleDeleteKey(element.id)}
/>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 9be3043

Please sign in to comment.