From e453a54c7ffd683b469b7d24c23d06272398b64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Zaninotto?= Date: Wed, 6 Nov 2024 00:20:33 +0100 Subject: [PATCH] Even simpler guesser --- packages/demo/src/App.tsx | 10 +- packages/demo/src/authProvider.ts | 20 -- packages/demo/src/dashboard/Avatar.tsx | 16 -- packages/demo/src/dashboard/Dashboard.tsx | 20 -- packages/demo/src/dashboard/HotContacts.tsx | 65 ------- packages/demo/src/dashboard/LatestNotes.tsx | 142 -------------- packages/demo/src/dashboard/Task.tsx | 180 ------------------ packages/demo/src/dashboard/TasksIterator.tsx | 29 --- packages/demo/src/dashboard/TasksList.tsx | 89 --------- packages/demo/src/dataProvider.ts | 8 - packages/demo/src/supabase.ts | 6 - packages/ra-supabase/src/AdminGuesser.tsx | 38 +++- 12 files changed, 36 insertions(+), 587 deletions(-) delete mode 100644 packages/demo/src/authProvider.ts delete mode 100644 packages/demo/src/dashboard/Avatar.tsx delete mode 100644 packages/demo/src/dashboard/Dashboard.tsx delete mode 100644 packages/demo/src/dashboard/HotContacts.tsx delete mode 100644 packages/demo/src/dashboard/LatestNotes.tsx delete mode 100644 packages/demo/src/dashboard/Task.tsx delete mode 100644 packages/demo/src/dashboard/TasksIterator.tsx delete mode 100644 packages/demo/src/dashboard/TasksList.tsx delete mode 100644 packages/demo/src/dataProvider.ts delete mode 100644 packages/demo/src/supabase.ts diff --git a/packages/demo/src/App.tsx b/packages/demo/src/App.tsx index 68b2b22..d66d49c 100644 --- a/packages/demo/src/App.tsx +++ b/packages/demo/src/App.tsx @@ -1,17 +1,11 @@ -import * as React from 'react'; import { BrowserRouter } from 'react-router-dom'; import { AdminGuesser } from 'ra-supabase'; -import { authProvider } from './authProvider'; -import { Dashboard } from './dashboard/Dashboard'; -import { dataProvider } from './dataProvider'; const App = () => ( ); diff --git a/packages/demo/src/authProvider.ts b/packages/demo/src/authProvider.ts deleted file mode 100644 index 4b31de8..0000000 --- a/packages/demo/src/authProvider.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { supabaseAuthProvider } from 'ra-supabase'; -import { supabase } from './supabase'; - -export const authProvider = supabaseAuthProvider(supabase, { - getIdentity: async user => { - const { data, error } = await supabase - .from('sales') - .select('id, first_name, last_name') - .ilike('email', user.email as string) - .single(); - - if (!data || error) { - throw new Error(); - } - return { - id: data.id, - fullName: `${data.first_name} ${data.last_name}`, - }; - }, -}); diff --git a/packages/demo/src/dashboard/Avatar.tsx b/packages/demo/src/dashboard/Avatar.tsx deleted file mode 100644 index 0a5d376..0000000 --- a/packages/demo/src/dashboard/Avatar.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; -import { Avatar as MuiAvatar } from '@mui/material'; -import { useRecordContext } from 'react-admin'; - -import { Contact } from '../types'; - -export const Avatar = (props: { record?: Contact }) => { - const record = useRecordContext(props); - if (!record) return null; - return ( - - {record.first_name.charAt(0)} - {record.last_name.charAt(0)} - - ); -}; diff --git a/packages/demo/src/dashboard/Dashboard.tsx b/packages/demo/src/dashboard/Dashboard.tsx deleted file mode 100644 index 6c86cba..0000000 --- a/packages/demo/src/dashboard/Dashboard.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import * as React from 'react'; -import { Grid } from '@mui/material'; - -import { HotContacts } from './HotContacts'; -import { LatestNotes } from './LatestNotes'; -import { TasksList } from './TasksList'; - -export const Dashboard = () => ( - - - - - - - - - - - -); diff --git a/packages/demo/src/dashboard/HotContacts.tsx b/packages/demo/src/dashboard/HotContacts.tsx deleted file mode 100644 index 2c7e73d..0000000 --- a/packages/demo/src/dashboard/HotContacts.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import * as React from 'react'; -import { Card, Box } from '@mui/material'; -import ContactsIcon from '@mui/icons-material/Contacts'; -import { useGetList, Link, SimpleList, useGetIdentity } from 'react-admin'; -import { formatDistance } from 'date-fns'; - -import { Avatar } from './Avatar'; -import { Contact } from '../types'; - -export const HotContacts = () => { - const { identity } = useGetIdentity(); - const { - data: contactData, - total: contactTotal, - isLoading: contactsLoading, - } = useGetList( - 'contacts', - { - pagination: { page: 1, perPage: 10 }, - sort: { field: 'last_seen', order: 'DESC' }, - filter: { status: 'hot', sales_id: identity?.id }, - }, - { enabled: Number.isInteger(identity?.id) } - ); - return ( -
- - - - - - Hot contacts - - - - - linkType="show" - data={contactData} - total={contactTotal} - isLoading={contactsLoading} - primaryText={contact => - `${contact.first_name} ${contact.last_name}` - } - resource="contacts" - secondaryText={(contact: Contact) => - formatDistance( - new Date(contact.last_seen), - new Date(), - { - addSuffix: true, - } - ) - } - leftAvatar={contact => } - dense - /> - -
- ); -}; diff --git a/packages/demo/src/dashboard/LatestNotes.tsx b/packages/demo/src/dashboard/LatestNotes.tsx deleted file mode 100644 index d4aede9..0000000 --- a/packages/demo/src/dashboard/LatestNotes.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import * as React from 'react'; -import { Card, CardContent, Typography, Box } from '@mui/material'; -import NoteIcon from '@mui/icons-material/Note'; -import { - useGetList, - useGetIdentity, - ReferenceField, - TextField, - FunctionField, -} from 'react-admin'; -import { formatDistance } from 'date-fns'; - -import { Contact as ContactType } from '../types'; - -export const LatestNotes = () => { - const { identity } = useGetIdentity(); - const { data: contactNotesData, isLoading: contactNotesLoading } = - useGetList( - 'contactNotes', - { - pagination: { page: 1, perPage: 5 }, - sort: { field: 'date', order: 'DESC' }, - filter: { sales_id: identity?.id }, - }, - { enabled: Number.isInteger(identity?.id) } - ); - const { data: dealNotesData, isLoading: dealNotesLoading } = useGetList( - 'dealNotes', - { - pagination: { page: 1, perPage: 5 }, - sort: { field: 'date', order: 'DESC' }, - filter: { sales_id: identity?.id }, - }, - { enabled: Number.isInteger(identity?.id) } - ); - if (contactNotesLoading || dealNotesLoading) { - return null; - } - // TypeScript guards - if (!contactNotesData || !dealNotesData) { - return null; - } - - const allNotes = ([] as any[]) - .concat( - contactNotesData.map(note => ({ - ...note, - type: 'contactNote', - })), - dealNotesData.map(note => ({ ...note, type: 'dealNote' })) - ) - .sort((a, b) => new Date(b.date).valueOf() - new Date(a.date).valueOf()) - .slice(0, 5); - - return ( -
- - - - - - My Latest Notes - - - - - {allNotes.map(note => ( - - - on{' '} - {note.type === 'dealNote' ? ( - - ) : ( - - )} - , added{' '} - {formatDistance( - new Date(note.date), - new Date(), - { - addSuffix: true, - } - )} - -
- - {note.text} - -
-
- ))} -
-
-
- ); -}; - -const Deal = ({ note }: any) => ( - <> - Deal{' '} - - - - -); - -const Contact = ({ note }: any) => ( - <> - Contact{' '} - - - variant="body2" - render={contact => `${contact.first_name} ${contact.last_name}`} - /> - - -); diff --git a/packages/demo/src/dashboard/Task.tsx b/packages/demo/src/dashboard/Task.tsx deleted file mode 100644 index d9feee7..0000000 --- a/packages/demo/src/dashboard/Task.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import * as React from 'react'; -import { useState, MouseEvent } from 'react'; -import { - DateField, - ReferenceField, - useUpdate, - useDeleteWithUndoController, -} from 'react-admin'; -import { - Checkbox, - IconButton, - ListItem, - ListItemButton, - ListItemIcon, - ListItemText, - Menu, - MenuItem, - Typography, -} from '@mui/material'; -import MoreVertIcon from '@mui/icons-material/MoreVert'; - -export const Task = ({ - task, - showContact, -}: { - task: any; - showContact?: boolean; -}) => { - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - const handleClick = (event: MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; - const [update, { isLoading: isUpdatePending }] = useUpdate(); - const { handleDelete } = useDeleteWithUndoController({ - record: task, - redirect: false, - }); - - const handleCheck = () => () => { - update('tasks', { - id: task.id, - data: { - done_date: task.done_date ? null : new Date().toISOString(), - }, - previousData: task, - }); - }; - const labelId = `checkbox-list-label-${task.id}`; - return ( - - - - - - { - update('tasks', { - id: task.id, - data: { - due_date: new Date( - Date.now() + 24 * 60 * 60 * 1000 - ) - .toISOString() - .slice(0, 10), - }, - previousData: task, - }); - handleClose(); - }} - > - Postpone to tomorrow - - { - update('tasks', { - id: task.id, - data: { - due_date: new Date( - Date.now() + 7 * 24 * 60 * 60 * 1000 - ) - .toISOString() - .slice(0, 10), - }, - previousData: task, - }); - handleClose(); - }} - > - Postpone to next week - - Delete - - - } - disableGutters - sx={{ pr: 3 }} - > - - - - - - {task.type && task.type !== 'None' && ( - <> - {task.type}  - - )} - {task.text} - - due - {showContact && ( - <> -  (Re:{' '} - - ) - - )} - - - - - ); -}; diff --git a/packages/demo/src/dashboard/TasksIterator.tsx b/packages/demo/src/dashboard/TasksIterator.tsx deleted file mode 100644 index 07c74c2..0000000 --- a/packages/demo/src/dashboard/TasksIterator.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import * as React from 'react'; -import { useListContext } from 'react-admin'; -import { isAfter } from 'date-fns'; -import { List } from '@mui/material'; - -import { Task } from './Task'; - -export const TasksIterator = ({ showContact }: { showContact?: boolean }) => { - const { data, error, isLoading } = useListContext(); - if (isLoading || error || !data || data.length === 0) return null; - - // Keep only tasks that are not done or done less than 5 minutes ago - const tasks = data.filter( - task => - !task.done_date || - isAfter( - new Date(task.done_date), - new Date(Date.now() - 5 * 60 * 1000) - ) - ); - - return ( - - {tasks.map(task => ( - - ))} - - ); -}; diff --git a/packages/demo/src/dashboard/TasksList.tsx b/packages/demo/src/dashboard/TasksList.tsx deleted file mode 100644 index 66928c7..0000000 --- a/packages/demo/src/dashboard/TasksList.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import * as React from 'react'; -import { Card, Box, Button } from '@mui/material'; -import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn'; -import { - useGetList, - Link, - useGetIdentity, - useList, - ListContextProvider, - ResourceContextProvider, -} from 'react-admin'; -import { TasksIterator } from './TasksIterator'; - -import { Contact } from '../types'; - -export const TasksList = () => { - const { identity } = useGetIdentity(); - - // get all the contacts for this sales - const { data: contacts, isLoading: contactsLoading } = useGetList( - 'contacts', - { - pagination: { page: 1, perPage: 500 }, - filter: { sales_id: identity?.id }, - }, - { enabled: !!identity } - ); - - // get the first 100 upcoming tasks for these contacts - const { data: tasks, isLoading: tasksLoading } = useGetList( - 'tasks', - { - pagination: { page: 1, perPage: 100 }, - sort: { field: 'due_date', order: 'ASC' }, - filter: { - 'done_date@is': 'null', - 'contact_id@in': `(${contacts - ?.map(contact => contact.id) - .join(',')})`, - }, - }, - { enabled: !!contacts } - ); - - const isPending = tasksLoading || contactsLoading; - // limit to 10 tasks and provide the list context - const listContext = useList({ - data: tasks, - resource: 'tasks', - perPage: 10, - }); - - if (isPending || !tasks || !contacts) return null; - - return ( -
- - - - - - Upcoming tasks - - - - - - - - - {!isPending && ( - - )} - -
- ); -}; diff --git a/packages/demo/src/dataProvider.ts b/packages/demo/src/dataProvider.ts deleted file mode 100644 index 0ee9781..0000000 --- a/packages/demo/src/dataProvider.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { supabaseDataProvider } from 'ra-supabase'; -import { supabase } from './supabase'; - -export const dataProvider = supabaseDataProvider({ - instanceUrl: import.meta.env.VITE_SUPABASE_URL, - apiKey: import.meta.env.VITE_SUPABASE_ANON_KEY, - supabaseClient: supabase, -}); diff --git a/packages/demo/src/supabase.ts b/packages/demo/src/supabase.ts deleted file mode 100644 index 35bd899..0000000 --- a/packages/demo/src/supabase.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createClient } from '@supabase/supabase-js'; - -export const supabase = createClient( - import.meta.env.VITE_SUPABASE_URL, - import.meta.env.VITE_SUPABASE_ANON_KEY -); diff --git a/packages/ra-supabase/src/AdminGuesser.tsx b/packages/ra-supabase/src/AdminGuesser.tsx index 169efb0..093696a 100644 --- a/packages/ra-supabase/src/AdminGuesser.tsx +++ b/packages/ra-supabase/src/AdminGuesser.tsx @@ -9,20 +9,26 @@ import { import type { AdminProps, AdminUIProps } from 'react-admin'; import { Route } from 'react-router-dom'; +import { supabaseDataProvider, supabaseAuthProvider } from 'ra-supabase-core'; import { useCrudGuesser, LoginPage, SetPasswordPage, ForgotPasswordPage, } from 'ra-supabase-ui-materialui'; +import { createClient } from '@supabase/supabase-js'; import { defaultI18nProvider } from './defaultI18nProvider'; -export const AdminGuesser = (props: AdminProps) => { +export const AdminGuesser = ( + props: AdminProps & { instanceUrl?: string; apiKey?: string } +) => { const { + instanceUrl, + apiKey, + dataProvider, authProvider, basename, darkTheme, - dataProvider, defaultTheme, i18nProvider = defaultI18nProvider, lightTheme, @@ -32,12 +38,27 @@ export const AdminGuesser = (props: AdminProps) => { ...rest } = props; + const defaultSupabaseClient = + instanceUrl && apiKey ? createClient(instanceUrl, apiKey) : null; + const defaultDataProvider = + instanceUrl && apiKey && defaultSupabaseClient + ? supabaseDataProvider({ + instanceUrl, + apiKey, + supabaseClient: defaultSupabaseClient, + }) + : undefined; + const defaultAuthProvider = + instanceUrl && apiKey && defaultSupabaseClient + ? supabaseAuthProvider(defaultSupabaseClient, {}) + : undefined; + return ( { import { Admin, Resource, CustomRoutes } from 'react-admin'; import { Route } from 'react-router-dom'; +import { createClient } from '@supabase/supabase-js'; import { CreateGuesser, EditGuesser, @@ -69,8 +91,16 @@ import { SetPasswordPage, ShowGuesser, defaultI18nProvider, + supabaseDataProvider, + supabaseAuthProvider } from 'ra-supabase'; +const instanceUrl = YOUR_SUPABASE_URL; +const apiKey = YOUR_SUPABASE_API_KEY; +const supabaseClient = createClient(instanceUrl, apiKey); +const dataProvider = supabaseDataProvider({ instanceUrl, apiKey, supabaseClient }); +const authProvider = supabaseAuthProvider(supabaseClient, {}); + export const App = () => (