Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
public/
tmp/
log/
vendor/
app/views/
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"trailingComma": "all",
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"printWidth": 100
}
5 changes: 2 additions & 3 deletions app/javascript/entrypoints/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@
// 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 `<head>` 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' %>
//
// 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.
//
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/entrypoints/s3_browser.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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";
@import 'bootstrap';
6 changes: 3 additions & 3 deletions app/javascript/entrypoints/s3_browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(<App />)
s3BrowserRoot.render(<App />);

console.log('S3 Browser React app load complete!');
console.log('S3 Browser React app load complete!');
6 changes: 3 additions & 3 deletions app/s3browser/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const App = () => {
<AppRouter />
</AppProvider>
</React.StrictMode>
)
}
);
};

export default App;
export default App;
2 changes: 1 addition & 1 deletion app/s3browser/src/app/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ export const AppProvider: FC<PropsWithChildren<unknown>> = ({ children }) => {
</ErrorBoundary>
</Suspense>
);
}
};
6 changes: 4 additions & 2 deletions app/s3browser/src/app/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: <RouteErrorFallback errorMessage="Error loading object details. Please try again." />,
errorElement: (
<RouteErrorFallback errorMessage="Error loading object details. Please try again." />
),
},
],
},
Expand All @@ -78,4 +80,4 @@ export const AppRouter = () => {
const router = useMemo(() => createAppRouter(queryClient), [queryClient]);

return <RouterProvider router={router} />;
};
};
22 changes: 12 additions & 10 deletions app/s3browser/src/app/routes/bucket-contents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -27,4 +29,4 @@ const BucketContentsRoute = () => {
);
};

export default BucketContentsRoute;
export default BucketContentsRoute;
2 changes: 1 addition & 1 deletion app/s3browser/src/app/routes/buckets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ const BucketsRoute = () => {
);
};

export default BucketsRoute;
export default BucketsRoute;
2 changes: 1 addition & 1 deletion app/s3browser/src/app/routes/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ const NotFoundRoute = () => {
);
};

export default NotFoundRoute;
export default NotFoundRoute;
23 changes: 14 additions & 9 deletions app/s3browser/src/app/routes/object-details.tsx
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -27,4 +32,4 @@ const ObjectDetailsRoute = () => {
);
};

export default ObjectDetailsRoute;
export default ObjectDetailsRoute;
2 changes: 1 addition & 1 deletion app/s3browser/src/components/errors/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export const MainErrorFallback = () => {
<h2>Ooops, something went wrong</h2>
</div>
);
};
};
20 changes: 11 additions & 9 deletions app/s3browser/src/components/errors/route-error.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,12 +13,14 @@ export const RouteErrorFallback: React.FC<RouteErrorFallbackProps> = ({ errorMes
<h3>Oops, something went wrong</h3>
<br />
<p>{errorMessage || 'The application encountered an error.'}</p>
{isRouteErrorResponse(error) && (
<Container>
<p>{error.status} {error.statusText}</p>
<pre>{JSON.stringify(error.data, null, 2)}</pre>
</Container>
)}
{isRouteErrorResponse(error) && (
<Container>
<p>
{error.status} {error.statusText}
</p>
<pre>{JSON.stringify(error.data, null, 2)}</pre>
</Container>
)}
</Container>
);
};
};
2 changes: 1 addition & 1 deletion app/s3browser/src/components/layouts/main-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ const MainLayout = () => {
);
};

export default MainLayout;
export default MainLayout;
39 changes: 13 additions & 26 deletions app/s3browser/src/components/ui/notifications/notification.tsx
Original file line number Diff line number Diff line change
@@ -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<NotificationType, string> = {
success: "success",
error: "danger",
warning: "warning",
info: "info",
success: 'success',
error: 'danger',
warning: 'warning',
info: 'info',
};

const ICON_MAP: Record<NotificationType, IconDefinition> = {
Expand Down Expand Up @@ -50,18 +44,11 @@ export const Notification = ({
autohide
delay={5000}
>
<Toast.Header
className={`bg-transparent border-${variant} border-opacity-25`}
>
<FontAwesomeIcon
icon={ICON_MAP[type]}
className={`me-2 text-${variant}-emphasis`}
/>
<Toast.Header className={`bg-transparent border-${variant} border-opacity-25`}>
<FontAwesomeIcon icon={ICON_MAP[type]} className={`me-2 text-${variant}-emphasis`} />
<strong className={`me-auto text-${variant}-emphasis`}>{title}</strong>
</Toast.Header>
{message && (
<Toast.Body className="text-body-secondary">{message}</Toast.Body>
)}
{message && <Toast.Body className="text-body-secondary">{message}</Toast.Body>}
</Toast>
);
};
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -16,4 +16,4 @@ export const Notifications = () => {
))}
</ToastContainer>
);
}
};
14 changes: 5 additions & 9 deletions app/s3browser/src/components/ui/table-builder/table-body.tsx
Original file line number Diff line number Diff line change
@@ -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<T> = {
table: ReturnType<typeof useReactTable<T>>;
columns: ColumnDef<T>[];
isLoading?: boolean;
}
};

const TableBody = <T extends object>({
table,
columns,
isLoading,
}: TableBodyProps<T>) => {
const TableBody = <T extends object>({ table, columns, isLoading }: TableBodyProps<T>) => {
if (isLoading) {
return (
<tbody>
Expand Down Expand Up @@ -50,4 +46,4 @@ const TableBody = <T extends object>({
);
};

export default TableBody;
export default TableBody;
Loading
Loading