feature: Created bucket viewer component

This commit is contained in:
leonardo.farias 2023-11-20 23:50:04 -03:00
parent 73c7eaf145
commit 03960c8bac
4 changed files with 194 additions and 6 deletions

47
package-lock.json generated
View File

@ -25726,6 +25726,27 @@
"resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz",
"integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw=="
},
"node_modules/fast-xml-parser": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz",
"integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
},
{
"type": "paypal",
"url": "https://paypal.me/naturalintelligence"
}
],
"dependencies": {
"strnum": "^1.0.5"
},
"bin": {
"fxparser": "src/cli/cli.js"
}
},
"node_modules/fastest-stable-stringify": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz",
@ -43382,6 +43403,11 @@
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/strnum": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
},
"node_modules/strong-log-transformer": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz",
@ -48307,6 +48333,7 @@
"@tanstack/react-table": "^8.8.5",
"ag-grid-react": "^30.0.4",
"chroma-js": "^2.4.2",
"fast-xml-parser": "^4.3.2",
"flexsearch": "0.7.21",
"leaflet": "^1.9.4",
"next-mdx-remote": "^4.4.1",
@ -48734,7 +48761,7 @@
},
"packages/core": {
"name": "@portaljs/core",
"version": "1.0.6",
"version": "1.0.8",
"license": "MIT",
"dependencies": {
"@docsearch/react": "^3.3.3",
@ -48773,7 +48800,7 @@
},
"packages/remark-embed": {
"name": "@portaljs/remark-embed",
"version": "1.0.4",
"version": "1.0.5",
"license": "MIT",
"dependencies": {
"unist-util-visit": "^4.1.1"
@ -48781,7 +48808,7 @@
},
"packages/remark-wiki-link": {
"name": "@portaljs/remark-wiki-link",
"version": "1.1.0",
"version": "1.1.2",
"license": "MIT",
"dependencies": {
"mdast-util-to-markdown": "^1.5.0",
@ -57179,6 +57206,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"eslint-plugin-storybook": "^0.6.11",
"fast-xml-parser": "^4.3.2",
"flexsearch": "0.7.21",
"json": "^11.0.0",
"leaflet": "^1.9.4",
@ -68614,6 +68642,14 @@
"resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz",
"integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw=="
},
"fast-xml-parser": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz",
"integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==",
"requires": {
"strnum": "^1.0.5"
}
},
"fastest-stable-stringify": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz",
@ -81832,6 +81868,11 @@
"acorn": "^8.8.2"
}
},
"strnum": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
},
"strong-log-transformer": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz",

View File

@ -29,14 +29,18 @@
"@githubocto/flat-ui": "^0.14.1",
"@heroicons/react": "^2.0.17",
"@planet/maps": "^8.1.0",
"@react-pdf-viewer/core": "3.6.0",
"@react-pdf-viewer/default-layout": "3.6.0",
"@tanstack/react-table": "^8.8.5",
"ag-grid-react": "^30.0.4",
"chroma-js": "^2.4.2",
"fast-xml-parser": "^4.3.2",
"flexsearch": "0.7.21",
"leaflet": "^1.9.4",
"next-mdx-remote": "^4.4.1",
"ol": "^7.4.0",
"papaparse": "^5.4.1",
"pdfjs-dist": "2.15.349",
"postcss-url": "^10.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@ -47,9 +51,6 @@
"vega": "5.25.0",
"vega-lite": "5.1.0",
"vitest": "^0.31.4",
"@react-pdf-viewer/core": "3.6.0",
"@react-pdf-viewer/default-layout": "3.6.0",
"pdfjs-dist": "2.15.349",
"xlsx": "^0.18.5"
},
"devDependencies": {

View File

@ -0,0 +1,112 @@
import { useEffect, useState } from 'react';
import LoadingSpinner from './LoadingSpinner';
import { XMLParser } from 'fast-xml-parser';
export interface BucketViewerProps {
domain: string;
suffix?: string;
}
interface BucketResponse {
ListBucketResult: ListBucketResult;
}
interface ListBucketResult {
Name: string;
Prefix: string;
MaxKeys: number;
IsTruncated: boolean;
Contents: Content[];
Marker: string;
NextMarker: string;
}
interface Content {
Key: string;
LastModified: string;
ETag: string;
Size: number;
StorageClass: StorageClass;
Owner?: Owner;
Type: Type;
}
interface Owner {
ID: number;
DisplayName: number;
}
enum StorageClass {
Standard = 'STANDARD',
}
enum Type {
Normal = 'Normal',
}
export function BucketViewer({ domain, suffix }: BucketViewerProps) {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [bucket, setBucket] = useState<BucketResponse>();
suffix = suffix ?? '/';
useEffect(() => {
setIsLoading(true);
fetch(`${domain}${suffix}`)
.then((res) => res.text())
.then((res) => {
const parsedXml: BucketResponse = new XMLParser().parse(res);
let {
ListBucketResult: { Contents },
} = parsedXml;
Contents = Contents ?? [];
parsedXml.ListBucketResult.Contents = Array.isArray(Contents)
? Contents
: [Contents];
setBucket(parsedXml);
})
.finally(() => setIsLoading(false));
}, [domain, suffix]);
return isLoading ? (
<div className="w-full flex items-center justify-center h-[300px]">
<LoadingSpinner />
</div>
) : bucket ? (
<>
{...bucket?.ListBucketResult?.Contents?.map((c, i) => (
<ul
onClick={() => {
const anchorId = `download_anchor_${i}`;
const a: HTMLAnchorElement =
(document.getElementById(anchorId) as HTMLAnchorElement | null) ??
document.createElement('a');
a.id = anchorId;
if (a.download) a.click();
else {
setIsLoading(true);
fetch(`${domain}${suffix}${c.Key}`)
.then((res) => res.blob())
.then((res) => {
a.href = URL.createObjectURL(res);
a.download = res.name ?? c.ETag.replace(/\"/g, '');
document.body.appendChild(a);
a.click();
})
.finally(() => setIsLoading(false));
}
}}
key={i}
className="mb-2 border-b-[2px] border-b-[red] hover:cursor-pointer"
>
<li>{c.Key}</li>
<li>{c.ETag}</li>
<li>{c.Owner?.DisplayName}</li>
<li>{c.Owner?.ID}</li>
<li>{c.Size}</li>
<li>{c.StorageClass}</li>
<li>{c.Type}</li>
<li>{c.LastModified}</li>
</ul>
))}
</>
) : null;
}

View File

@ -0,0 +1,34 @@
import type { Meta, StoryObj } from '@storybook/react';
import { BucketViewer, BucketViewerProps } from '../src/components/BucketViewer';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/BucketViewer',
component: BucketViewer,
tags: ['autodocs'],
argTypes: {
domain: {
description:
'Bucket domain URI',
},
suffix: {
description:
'Suffix of bucket domain',
},
},
};
export default meta;
type Story = StoryObj<BucketViewerProps>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Normal: Story = {
name: 'Bucket viewer',
args: {
domain: 'https://nwguide.fra1.digitaloceanspaces.com',
suffix: '/'
},
};