Skip to content

Commit

Permalink
refactoring data-sources lib
Browse files Browse the repository at this point in the history
  • Loading branch information
AppElent committed Jan 22, 2025
1 parent ddb2c0e commit 1a45efa
Show file tree
Hide file tree
Showing 23 changed files with 696 additions and 742 deletions.
6 changes: 5 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ const firebaseProvider = new FirebaseAuthProvider({
});

const dataSources = {
settings: new LocalStorageDataSource({ target: 'settings', targetMode: 'document' }),
settings: new LocalStorageDataSource({
target: 'settings',
targetMode: 'document',
subscribe: true,
}),
};

function App() {
Expand Down
88 changes: 88 additions & 0 deletions src/components/default/data-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TableSortLabel,
} from '@mui/material';
import React, { useState } from 'react';

interface DataTableProps {
columns: {
id: string;
accessor: string;
label: string;
render?: (value: any, row: any, rowIndex: number) => any;
}[];
data: any[];
}

const DataTable = ({ columns, data }: DataTableProps) => {
// State to manage sorting
const [order, setOrder] = useState<'asc' | 'desc' | undefined>('asc'); // 'asc' or 'desc'
const [orderBy, setOrderBy] = useState<string | null>(null); // column to sort by

// Helper function to handle sorting logic
const handleRequestSort = (property: string) => {
const isAsc = orderBy === property && order === 'asc';
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(property);
};

// Function to sort data based on the current sorting state
const sortedData = React.useMemo(() => {
if (orderBy === null) return data; // No sorting

return [...data].sort((a, b) => {
const aValue = a[orderBy];
const bValue = b[orderBy];
if (aValue < bValue) {
return order === 'asc' ? -1 : 1;
}
if (aValue > bValue) {
return order === 'asc' ? 1 : -1;
}
return 0;
});
}, [data, orderBy, order]);

return (
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell key={column.id}>
<TableSortLabel
active={orderBy === column.accessor}
direction={orderBy === column.accessor ? order : 'asc'}
onClick={() => handleRequestSort(column.accessor)}
>
{column.label}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{sortedData.map((row, rowIndex) => (
<TableRow key={rowIndex}>
{columns.map((column) => (
<TableCell key={column.id}>
{column.render
? column.render(row[column.accessor], row, rowIndex)
: row[column.accessor]}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
};

export default DataTable;
8 changes: 3 additions & 5 deletions src/components/default/images/image-cropper.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-nocheck

import useIsMobile from '@/hooks/use-is-mobile';
import CloseIcon from '@mui/icons-material/Close';
import {
Expand Down Expand Up @@ -30,7 +28,7 @@ async function convertImageToDataURL(url: string): Promise<string> {
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onload = () => resolve(reader.result as string);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
Expand Down Expand Up @@ -69,15 +67,15 @@ const ImageCropper = ({ imageUrl, filename, onSave, dialog, cropperProps }: Imag
return path.split('/').pop() || 'unknown-filename.jpg';
};

const onCropComplete = useCallback((_, croppedAreaPixels: any) => {
const onCropComplete = useCallback((_: any, croppedAreaPixels: any) => {
setCroppedAreaPixels(croppedAreaPixels);
}, []);

const saveCroppedImage = async () => {
if (imageUrl && croppedAreaPixels) {
const croppedImageBlob = await getCroppedImg(imageUrl, croppedAreaPixels);

const croppedFile = new File([croppedImageBlob], getFileName(filename), {
const croppedFile = new File([croppedImageBlob as Blob], getFileName(filename), {
type: 'image/jpeg',
});
const croppedFileUrl = await onSave(croppedFile, filename);
Expand Down
12 changes: 5 additions & 7 deletions src/components/default/images/image-uploader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-nocheck

import ImageCropper from '@/components/default/images/image-cropper';
import useDialog from '@/hooks/use-dialog';
import { Button } from '@mui/material';
Expand Down Expand Up @@ -73,14 +71,14 @@ const ImageUploader = ({
thumbnail,
multiple = false,
}: ImageUploaderProps) => {
const [imageSrc, setImageSrc] = useState<string>(null);
const [imageSrc, setImageSrc] = useState<string | null>(null);
const dialog = useDialog();

const getFileName = (path: string): string => {
return path.split('/').pop() || 'unknown-filename.jpg';
};

const processFile = async (file) => {
const processFile = async (file: File) => {
if (file) {
if (file.size > max_size) {
console.log(
Expand All @@ -105,7 +103,7 @@ const ImageUploader = ({

if (thumbnail?.path) {
const thumbnailBlob = await createThumbnail(file);
const thumbnailFile = new File([thumbnailBlob], getFileName(thumbnail.path), {
const thumbnailFile = new File([thumbnailBlob as Blob], getFileName(thumbnail.path), {
type: 'image/jpeg',
});
const uploadFunction = thumbnail.uploadFile || uploadFile;
Expand Down Expand Up @@ -175,8 +173,8 @@ const ImageUploader = ({
</Button>
</label>
<ImageCropper
imageUrl={imageSrc}
filename={cropObject?.path}
imageUrl={imageSrc as string}
filename={cropObject?.path as string}
onSave={cropObject?.uploadFile || uploadFile}
dialog={{ isOpen: !!imageSrc, close: () => setImageSrc(null) }}
cropperProps={cropObject?.props}
Expand Down
53 changes: 27 additions & 26 deletions src/libs/data-sources/DataProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import React, { createContext, ReactNode, useCallback, useEffect, useState } from 'react';
import { DataSource, DataSourceObject } from '.';

// interface DataProviderContextProps {
// dataSources: DataSourceObject;
// setDataSource: (key: string, dataSource: DataSource<any>) => void;
// addDataSource: (key: string, dataSource: DataSource<any>) => void;
// data: Record<string, any>;
// loading: Record<string, boolean>;
// error: Record<string, any>;
// fetchData: (key: string, filter?: object) => Promise<void>;
// subscribeToData: (key: string) => void;
// subscriptions: Record<string, () => void>;
// add: (key: string, item: any) => Promise<any>;
// update: (key: string, data: any, id: string) => Promise<void>;
// set: (key: string, data: any, id: string) => Promise<void>;
// remove: (key: string, id: string) => Promise<void>;
// } // TODO: implement?
import { DataSourceObject, FilterObject } from '.';
import BaseDataSource from './data-sources/BaseDataSource';

interface DataProviderContextProps {
dataSources: DataSourceObject;
setDataSource: (key: string, dataSource: BaseDataSource<any>) => void;
addDataSource: (key: string, dataSource: BaseDataSource<any>) => void;
data: Record<string, any>;
loading: Record<string, boolean>;
error: Record<string, any>;
fetchData: (key: string, filter?: object) => Promise<void>;
subscribeToData: (key: string) => void;
subscriptions: Record<string, () => void>;
add: (key: string, item: any) => Promise<any>;
update: (key: string, data: any, id?: string) => Promise<any>;
set: (key: string, data: any, id?: string) => Promise<any>;
remove: (key: string, id?: string) => Promise<void>;
} // TODO: implement?

// Create a context for the data
// eslint-disable-next-line react-refresh/only-export-components
export const DataContext = createContext<any>(undefined);
export const DataContext = createContext<DataProviderContextProps | undefined>(undefined);

interface DataProviderProps {
dataSources: DataSourceObject;
Expand All @@ -34,14 +35,14 @@ const DataProvider: React.FC<DataProviderProps> = ({ dataSources, children }) =>
const [dataSourcesState, setDataSourcesState] = useState(dataSources);

const addDataSource = useCallback(
(key: string, newDataSource: DataSource<any>) => {
(key: string, newDataSource: BaseDataSource<any>) => {
setDataSourcesState((prev) => ({ ...prev, [key]: newDataSource }));
},
[setDataSourcesState]
);

const setDataSource = useCallback((key: string, dataSource: DataSource<any>) => {
setDataSourcesState((prev) => ({ ...prev, [key]: { ...prev[key], dataSource } }));
const setDataSource = useCallback((key: string, dataSource: BaseDataSource<any>) => {
setDataSourcesState((prev) => ({ ...prev, [key]: dataSource }));
}, []);

const getDataSource = useCallback(
Expand All @@ -54,15 +55,15 @@ const DataProvider: React.FC<DataProviderProps> = ({ dataSources, children }) =>
);

const fetchData = useCallback(
async (key: string, filter?: object) => {
async (key: string, _filter?: FilterObject<any>) => {
if (subscriptions[key]) return; // Skip if there is an active subscription
setLoading((prev) => ({ ...prev, [key]: true }));
setError((prev) => ({ ...prev, [key]: null }));
try {
const dataSource = getDataSource(key);
const result =
dataSource.options?.targetMode === 'collection'
? await dataSource.getAll(filter)
? await dataSource.getAll() // TODO: add filter
: await dataSource.get();
setData((prev) => ({ ...prev, [key]: result }));
} catch (err) {
Expand Down Expand Up @@ -108,7 +109,7 @@ const DataProvider: React.FC<DataProviderProps> = ({ dataSources, children }) =>
);

const update = useCallback(
async (key: string, data: any, id: string) => {
async (key: string, data: any, id?: string) => {
const dataSource = getDataSource(key);
const newData = await dataSource.update(data, id);
if (!subscriptions[key] && data[key]) {
Expand All @@ -124,7 +125,7 @@ const DataProvider: React.FC<DataProviderProps> = ({ dataSources, children }) =>
);

const set = useCallback(
async (key: string, data: any, id: string) => {
async (key: string, data: any, id?: string) => {
const dataSource = getDataSource(key);
await dataSource.set(data, id);
if (!subscriptions[key] && data[key]) {
Expand All @@ -138,7 +139,7 @@ const DataProvider: React.FC<DataProviderProps> = ({ dataSources, children }) =>
);

const remove = useCallback(
async (key: string, id: string) => {
async (key: string, id?: string) => {
const dataSource = getDataSource(key);
await dataSource.delete(id);
if (!subscriptions[key] && data[key]) {
Expand Down
Loading

0 comments on commit 1a45efa

Please sign in to comment.