diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..af63504
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,6 @@
+node_modules/
+public/
+tmp/
+log/
+vendor/
+app/views/
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..cea34cb
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,8 @@
+{
+ "trailingComma": "all",
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": true,
+ "printWidth": 100
+}
\ No newline at end of file
diff --git a/app/javascript/entrypoints/application.ts b/app/javascript/entrypoints/application.ts
index 9ee6218..5efe348 100644
--- a/app/javascript/entrypoints/application.ts
+++ b/app/javascript/entrypoints/application.ts
@@ -2,13 +2,12 @@
// The S3 Browser React application uses the s3_browser.html.erb layout, which
// loads the s3_browser.tsx entrypoint file.
-
// To see this message, add the following to the `
` section in your
// views/layouts/application.html.erb
//
// <%= vite_client_tag %>
// <%= vite_javascript_tag 'application' %>
-console.log('Vite ⚡️ Rails')
+console.log('Vite ⚡️ Rails');
// If using a TypeScript entrypoint file:
// <%= vite_typescript_tag 'application' %>
@@ -16,7 +15,7 @@ console.log('Vite ⚡️ Rails')
// If you want to use .jsx or .tsx, add the extension:
// <%= vite_javascript_tag 'application.jsx' %>
-console.log('Visit the guide for more information: ', 'https://vite-ruby.netlify.app/guide/rails')
+console.log('Visit the guide for more information: ', 'https://vite-ruby.netlify.app/guide/rails');
// Example: Load Rails libraries in Vite.
//
diff --git a/app/javascript/entrypoints/s3_browser.scss b/app/javascript/entrypoints/s3_browser.scss
index 1ac573f..abbd7f1 100644
--- a/app/javascript/entrypoints/s3_browser.scss
+++ b/app/javascript/entrypoints/s3_browser.scss
@@ -9,4 +9,4 @@
// --bs-accordion-border-radius: Changes the border radius.
// --bs-accordion-btn-focus-box-shadow: Removes the blue outline when an item is focused. */
-@import "bootstrap";
\ No newline at end of file
+@import 'bootstrap';
diff --git a/app/javascript/entrypoints/s3_browser.tsx b/app/javascript/entrypoints/s3_browser.tsx
index 2eea355..548d47f 100644
--- a/app/javascript/entrypoints/s3_browser.tsx
+++ b/app/javascript/entrypoints/s3_browser.tsx
@@ -2,9 +2,9 @@ import { createRoot } from 'react-dom/client';
import App from '../../s3browser/src/app/App';
const s3BrowserAppElement = document.getElementById('s3-browser-app');
-if (!s3BrowserAppElement ) throw new Error('S3 Browser app root element not found');
+if (!s3BrowserAppElement) throw new Error('S3 Browser app root element not found');
const s3BrowserRoot = createRoot(s3BrowserAppElement);
-s3BrowserRoot .render()
+s3BrowserRoot.render();
-console.log('S3 Browser React app load complete!');
\ No newline at end of file
+console.log('S3 Browser React app load complete!');
diff --git a/app/s3browser/src/app/App.tsx b/app/s3browser/src/app/App.tsx
index 36aa83f..e1c9904 100644
--- a/app/s3browser/src/app/App.tsx
+++ b/app/s3browser/src/app/App.tsx
@@ -9,7 +9,7 @@ const App = () => {
- )
-}
+ );
+};
-export default App;
\ No newline at end of file
+export default App;
diff --git a/app/s3browser/src/app/provider.tsx b/app/s3browser/src/app/provider.tsx
index b9e62f6..9a75178 100644
--- a/app/s3browser/src/app/provider.tsx
+++ b/app/s3browser/src/app/provider.tsx
@@ -56,4 +56,4 @@ export const AppProvider: FC> = ({ children }) => {
);
-}
\ No newline at end of file
+};
diff --git a/app/s3browser/src/app/router.tsx b/app/s3browser/src/app/router.tsx
index 399556f..95d2638 100644
--- a/app/s3browser/src/app/router.tsx
+++ b/app/s3browser/src/app/router.tsx
@@ -58,7 +58,9 @@ export const createAppRouter = (queryClient: QueryClient) =>
path: ':bucketName/object-details',
lazy: () => import('./routes/object-details').then(convert(queryClient)),
// This route uses useSuspenseQuery, so we want to ensure any errors are caught by the route error boundary
- errorElement: ,
+ errorElement: (
+
+ ),
},
],
},
@@ -78,4 +80,4 @@ export const AppRouter = () => {
const router = useMemo(() => createAppRouter(queryClient), [queryClient]);
return ;
-};
\ No newline at end of file
+};
diff --git a/app/s3browser/src/app/routes/bucket-contents.tsx b/app/s3browser/src/app/routes/bucket-contents.tsx
index f6dd263..2ea2ca2 100644
--- a/app/s3browser/src/app/routes/bucket-contents.tsx
+++ b/app/s3browser/src/app/routes/bucket-contents.tsx
@@ -4,16 +4,18 @@ import { getBucketContentsQueryOptions } from '@/features/file-browser/api/get-b
import BucketContentsTable from '@/features/file-browser/components/bucket-contents-table';
import { normalizePrefix } from '@/features/file-browser/utils/format-utils';
-export const clientLoader = (queryClient: QueryClient) => async ({ params, request }: LoaderFunctionArgs) => {
- const bucketName = params.bucketName as string;
- const url = new URL(request.url);
- const prefix = normalizePrefix(url.searchParams.get('prefix') ?? '');
- const query = getBucketContentsQueryOptions(bucketName, prefix);
+export const clientLoader =
+ (queryClient: QueryClient) =>
+ async ({ params, request }: LoaderFunctionArgs) => {
+ const bucketName = params.bucketName as string;
+ const url = new URL(request.url);
+ const prefix = normalizePrefix(url.searchParams.get('prefix') ?? '');
+ const query = getBucketContentsQueryOptions(bucketName, prefix);
- // Our API returns results in ~1-2 seconds for large buckets, so we don't want to
- // await this and block the UI. Instead, we let the component handle the loading state.
- queryClient.prefetchQuery(query);
-};
+ // Our API returns results in ~1-2 seconds for large buckets, so we don't want to
+ // await this and block the UI. Instead, we let the component handle the loading state.
+ queryClient.prefetchQuery(query);
+ };
const BucketContentsRoute = () => {
const params = useParams();
@@ -27,4 +29,4 @@ const BucketContentsRoute = () => {
);
};
-export default BucketContentsRoute;
\ No newline at end of file
+export default BucketContentsRoute;
diff --git a/app/s3browser/src/app/routes/buckets.tsx b/app/s3browser/src/app/routes/buckets.tsx
index ef4f0e1..c58d812 100644
--- a/app/s3browser/src/app/routes/buckets.tsx
+++ b/app/s3browser/src/app/routes/buckets.tsx
@@ -17,4 +17,4 @@ const BucketsRoute = () => {
);
};
-export default BucketsRoute;
\ No newline at end of file
+export default BucketsRoute;
diff --git a/app/s3browser/src/app/routes/not-found.tsx b/app/s3browser/src/app/routes/not-found.tsx
index 8ed5656..8e1e14a 100644
--- a/app/s3browser/src/app/routes/not-found.tsx
+++ b/app/s3browser/src/app/routes/not-found.tsx
@@ -10,4 +10,4 @@ const NotFoundRoute = () => {
);
};
-export default NotFoundRoute;
\ No newline at end of file
+export default NotFoundRoute;
diff --git a/app/s3browser/src/app/routes/object-details.tsx b/app/s3browser/src/app/routes/object-details.tsx
index 553ad7d..f47b655 100644
--- a/app/s3browser/src/app/routes/object-details.tsx
+++ b/app/s3browser/src/app/routes/object-details.tsx
@@ -1,16 +1,21 @@
import { LoaderFunctionArgs, useParams, useSearchParams } from 'react-router';
import { QueryClient } from '@tanstack/react-query';
-import { getObjectDetailsQueryOptions, useObjectDetailsSuspenseQuery } from '@/features/file-browser/api/get-object-details';
+import {
+ getObjectDetailsQueryOptions,
+ useObjectDetailsSuspenseQuery,
+} from '@/features/file-browser/api/get-object-details';
import ObjectDetailDisplay from '@/features/file-browser/components/object-detail-display';
-export const clientLoader = (queryClient: QueryClient) => async ({ params, request }: LoaderFunctionArgs) => {
- const bucketName = params.bucketName as string;
- const url = new URL(request.url);
- const key = url.searchParams.get('prefix') ?? '';
- const query = getObjectDetailsQueryOptions(bucketName, key);
+export const clientLoader =
+ (queryClient: QueryClient) =>
+ async ({ params, request }: LoaderFunctionArgs) => {
+ const bucketName = params.bucketName as string;
+ const url = new URL(request.url);
+ const key = url.searchParams.get('prefix') ?? '';
+ const query = getObjectDetailsQueryOptions(bucketName, key);
- await queryClient.prefetchQuery(query);
-};
+ await queryClient.prefetchQuery(query);
+ };
const ObjectDetailsRoute = () => {
const params = useParams();
@@ -27,4 +32,4 @@ const ObjectDetailsRoute = () => {
);
};
-export default ObjectDetailsRoute;
\ No newline at end of file
+export default ObjectDetailsRoute;
diff --git a/app/s3browser/src/components/errors/main.tsx b/app/s3browser/src/components/errors/main.tsx
index 71ba1a4..146ad6d 100644
--- a/app/s3browser/src/components/errors/main.tsx
+++ b/app/s3browser/src/components/errors/main.tsx
@@ -4,4 +4,4 @@ export const MainErrorFallback = () => {
Ooops, something went wrong
);
-};
\ No newline at end of file
+};
diff --git a/app/s3browser/src/components/errors/route-error.tsx b/app/s3browser/src/components/errors/route-error.tsx
index cdaf83c..29c48b2 100644
--- a/app/s3browser/src/components/errors/route-error.tsx
+++ b/app/s3browser/src/components/errors/route-error.tsx
@@ -1,5 +1,5 @@
-import { Container } from "react-bootstrap";
-import { useRouteError, isRouteErrorResponse } from "react-router";
+import { Container } from 'react-bootstrap';
+import { useRouteError, isRouteErrorResponse } from 'react-router';
type RouteErrorFallbackProps = {
errorMessage?: string;
@@ -13,12 +13,14 @@ export const RouteErrorFallback: React.FC = ({ errorMes
Oops, something went wrong
{errorMessage || 'The application encountered an error.'}
- {isRouteErrorResponse(error) && (
-
-
{error.status} {error.statusText}
-
{JSON.stringify(error.data, null, 2)}
-
- )}
+ {isRouteErrorResponse(error) && (
+
+
+ {error.status} {error.statusText}
+
+
{JSON.stringify(error.data, null, 2)}
+
+ )}
);
-};
\ No newline at end of file
+};
diff --git a/app/s3browser/src/components/layouts/main-layout.tsx b/app/s3browser/src/components/layouts/main-layout.tsx
index bff672c..29c4997 100644
--- a/app/s3browser/src/components/layouts/main-layout.tsx
+++ b/app/s3browser/src/components/layouts/main-layout.tsx
@@ -16,4 +16,4 @@ const MainLayout = () => {
);
};
-export default MainLayout;
\ No newline at end of file
+export default MainLayout;
diff --git a/app/s3browser/src/components/ui/notifications/notification.tsx b/app/s3browser/src/components/ui/notifications/notification.tsx
index a2e7069..97d76cb 100644
--- a/app/s3browser/src/components/ui/notifications/notification.tsx
+++ b/app/s3browser/src/components/ui/notifications/notification.tsx
@@ -1,22 +1,16 @@
-import { Toast } from "react-bootstrap";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import {
- faCircleCheck,
- faCircleXmark,
-} from "@fortawesome/free-solid-svg-icons";
-import {
- faTriangleExclamation,
- faCircleInfo
-} from "@fortawesome/free-solid-svg-icons";
-import type { IconDefinition } from "@fortawesome/fontawesome-svg-core";
+import { Toast } from 'react-bootstrap';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faCircleCheck, faCircleXmark } from '@fortawesome/free-solid-svg-icons';
+import { faTriangleExclamation, faCircleInfo } from '@fortawesome/free-solid-svg-icons';
+import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
-type NotificationType = "success" | "error" | "warning" | "info";
+type NotificationType = 'success' | 'error' | 'warning' | 'info';
const VARIANT_MAP: Record = {
- success: "success",
- error: "danger",
- warning: "warning",
- info: "info",
+ success: 'success',
+ error: 'danger',
+ warning: 'warning',
+ info: 'info',
};
const ICON_MAP: Record = {
@@ -50,18 +44,11 @@ export const Notification = ({
autohide
delay={5000}
>
-
-
+
+ {title}
- {message && (
- {message}
- )}
+ {message && {message}}
);
};
diff --git a/app/s3browser/src/components/ui/notifications/notifications.tsx b/app/s3browser/src/components/ui/notifications/notifications.tsx
index 3313429..3eee73e 100644
--- a/app/s3browser/src/components/ui/notifications/notifications.tsx
+++ b/app/s3browser/src/components/ui/notifications/notifications.tsx
@@ -1,6 +1,6 @@
-import { ToastContainer } from "react-bootstrap";
-import { useNotifications } from "@/stores/notifications-store";
-import { Notification } from "./notification";
+import { ToastContainer } from 'react-bootstrap';
+import { useNotifications } from '@/stores/notifications-store';
+import { Notification } from './notification';
export const Notifications = () => {
const { notifications, dismissNotification } = useNotifications();
@@ -16,4 +16,4 @@ export const Notifications = () => {
))}
);
-}
\ No newline at end of file
+};
diff --git a/app/s3browser/src/components/ui/table-builder/table-body.tsx b/app/s3browser/src/components/ui/table-builder/table-body.tsx
index 6112867..c3539cb 100644
--- a/app/s3browser/src/components/ui/table-builder/table-body.tsx
+++ b/app/s3browser/src/components/ui/table-builder/table-body.tsx
@@ -1,18 +1,14 @@
-import { ColumnDef, useReactTable } from '@tanstack/react-table'
+import { ColumnDef, useReactTable } from '@tanstack/react-table';
import Spinner from 'react-bootstrap/Spinner';
-import TableRow from './table-row'
+import TableRow from './table-row';
type TableBodyProps = {
table: ReturnType>;
columns: ColumnDef[];
isLoading?: boolean;
-}
+};
-const TableBody = ({
- table,
- columns,
- isLoading,
-}: TableBodyProps) => {
+const TableBody = ({ table, columns, isLoading }: TableBodyProps) => {
if (isLoading) {
return (
@@ -50,4 +46,4 @@ const TableBody = ({
);
};
-export default TableBody;
\ No newline at end of file
+export default TableBody;
diff --git a/app/s3browser/src/components/ui/table-builder/table-builder.tsx b/app/s3browser/src/components/ui/table-builder/table-builder.tsx
index 1c65c62..b0ef926 100644
--- a/app/s3browser/src/components/ui/table-builder/table-builder.tsx
+++ b/app/s3browser/src/components/ui/table-builder/table-builder.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react'
+import { useState } from 'react';
import {
getCoreRowModel,
@@ -9,17 +9,17 @@ import {
getPaginationRowModel,
PaginationState,
Updater,
-} from '@tanstack/react-table'
-import { Table as BTable } from 'react-bootstrap'
-import TableHeader from './table-header'
-import TablePagination from './table-pagination'
-import TableBody from './table-body'
+} from '@tanstack/react-table';
+import { Table as BTable } from 'react-bootstrap';
+import TableHeader from './table-header';
+import TablePagination from './table-pagination';
+import TableBody from './table-body';
interface TableBuilderProps {
- data: T[]
- columns: ColumnDef[]
- initialSorting?: SortingState,
- pageSize?: number,
+ data: T[];
+ columns: ColumnDef[];
+ initialSorting?: SortingState;
+ pageSize?: number;
pagination?: PaginationState;
onPaginationChange?: (updater: Updater) => void;
isLoading?: boolean;
@@ -37,22 +37,22 @@ function TableBuilder({
pageSize = DEFAULT_PAGE_SIZE,
pagination,
onPaginationChange,
- isLoading
+ isLoading,
}: TableBuilderProps) {
// You can disable sorting specific columns or specify custom sorting functions in the column definitions
// Docs: https://tanstack.com/table/latest/docs/api/features/sorting#column-def-options
- const [sorting, setSorting] = useState(initialSorting)
+ const [sorting, setSorting] = useState(initialSorting);
const [internalPagination, setInternalPagination] = useState({
pageIndex: 0,
pageSize,
- })
+ });
// Determine if pagination is controlled externally (via props) or internally (via component state)
const isPaginationControlled = pagination !== undefined && onPaginationChange !== undefined;
const effectivePagination = isPaginationControlled ? pagination : internalPagination;
const effectiveOnPaginationChange = isPaginationControlled
? onPaginationChange
- : setInternalPagination
+ : setInternalPagination;
const table = useReactTable({
data,
@@ -60,13 +60,13 @@ function TableBuilder({
getCoreRowModel: getCoreRowModel(),
state: {
sorting,
- pagination: effectivePagination
+ pagination: effectivePagination,
},
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
onPaginationChange: effectiveOnPaginationChange,
getPaginationRowModel: getPaginationRowModel(),
- })
+ });
return (
<>
@@ -76,18 +76,12 @@ function TableBuilder({
{table.getHeaderGroups().map((headerGroup) => (
-
+
))}
-
+
>
- )
+ );
}
-export default TableBuilder;
\ No newline at end of file
+export default TableBuilder;
diff --git a/app/s3browser/src/components/ui/table-builder/table-header.tsx b/app/s3browser/src/components/ui/table-builder/table-header.tsx
index c5413ff..52b63cd 100644
--- a/app/s3browser/src/components/ui/table-builder/table-header.tsx
+++ b/app/s3browser/src/components/ui/table-builder/table-header.tsx
@@ -1,29 +1,29 @@
-import { flexRender, Header, HeaderGroup } from '@tanstack/react-table'
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
-import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons'
+import { flexRender, Header, HeaderGroup } from '@tanstack/react-table';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons';
interface TableHeaderProps {
- headerGroup: HeaderGroup
+ headerGroup: HeaderGroup;
}
function TableHeader({ headerGroup }: TableHeaderProps) {
const renderSortingIcon = (sortDirection: 'asc' | 'desc' | null) => {
- const sharedClassNames = 'ms-2 flex-shrink-0 mt-1'
+ const sharedClassNames = 'ms-2 flex-shrink-0 mt-1';
if (sortDirection === 'asc') {
- return
+ return ;
}
if (sortDirection === 'desc') {
- return
+ return ;
}
- return
- }
+ return ;
+ };
const createColumnHeader = (header: Header) => {
- if (header.isPlaceholder) return null
+ if (header.isPlaceholder) return null;
- const sharedClassNames = 'fw-semibold d-inline-flex m-0 p-0 align-items-start text-start'
- const headerText = flexRender(header.column.columnDef.header, header.getContext())
+ const sharedClassNames = 'fw-semibold d-inline-flex m-0 p-0 align-items-start text-start';
+ const headerText = flexRender(header.column.columnDef.header, header.getContext());
if (header.column.getCanSort()) {
return (
@@ -35,23 +35,26 @@ function TableHeader({ headerGroup }: TableHeaderProps) {
{headerText}
{renderSortingIcon(header.column.getIsSorted() || null)}
- )
+ );
}
- return