diff --git a/studio/components/interfaces/Settings/Logs/LogsNavigation.tsx b/studio/components/interfaces/Settings/Logs/LogsNavigation.tsx index 6885ea51601..ebea8863af9 100644 --- a/studio/components/interfaces/Settings/Logs/LogsNavigation.tsx +++ b/studio/components/interfaces/Settings/Logs/LogsNavigation.tsx @@ -13,10 +13,12 @@ const LogsNavigation = () => { const indexRoute = 'query' const logsSavedQueries = useFlag('logsSavedQueries') + const logsRecentQueries = useFlag('logsRecentQueries') const tabs = [ { id: 'query', label: 'Query' }, logsSavedQueries ? { id: 'saved', label: 'Saved' } : null, + logsRecentQueries ? { id: 'recent', label: 'Recent' } : null, { id: 'templates', label: 'Templates' }, ].filter(Boolean) as TabItem[] diff --git a/studio/components/interfaces/Settings/Logs/RecentQueriesItem.tsx b/studio/components/interfaces/Settings/Logs/RecentQueriesItem.tsx new file mode 100644 index 00000000000..571613d84dd --- /dev/null +++ b/studio/components/interfaces/Settings/Logs/RecentQueriesItem.tsx @@ -0,0 +1,40 @@ +import { Button, IconPlay } from '@supabase/ui' +import Table from 'components/to-be-cleaned/Table' +import { useRouter } from 'next/router' +import { LogSqlSnippets } from 'types' + +interface Props { + item: LogSqlSnippets.Content +} + +const RecentQueriesItem: React.FC = ({ item }) => { + const router = useRouter() + const { ref } = router.query + + return ( + + +
+
{item.sql}
+
+
+ + + +
+ ) +} + +export default RecentQueriesItem diff --git a/studio/pages/project/[ref]/logs-explorer/index.tsx b/studio/pages/project/[ref]/logs-explorer/index.tsx index 2f76f7305f0..67711a8b1ad 100644 --- a/studio/pages/project/[ref]/logs-explorer/index.tsx +++ b/studio/pages/project/[ref]/logs-explorer/index.tsx @@ -86,6 +86,9 @@ export const LogsExplorerPage: NextPage = () => { pathname: router.pathname, query: { ...router.query, q: query }, }) + content.addRecentLogSqlSnippet({ + sql: query + }) } const handleClear = () => { diff --git a/studio/pages/project/[ref]/logs-explorer/recent.tsx b/studio/pages/project/[ref]/logs-explorer/recent.tsx new file mode 100644 index 00000000000..ae16b4630dc --- /dev/null +++ b/studio/pages/project/[ref]/logs-explorer/recent.tsx @@ -0,0 +1,71 @@ +import React, { useEffect } from 'react' +import { NextPage } from 'next' +import Link from 'next/link' +import { observer } from 'mobx-react-lite' +import { Button, IconClock, IconSave, Loading } from '@supabase/ui' +import { useStore, withAuth } from 'hooks' +import RecentQueriesItem from 'components/interfaces/Settings/Logs/RecentQueriesItem' +import LogsExplorerLayout from 'components/layouts/LogsExplorerLayout/LogsExplorerLayout' + +import Table from 'components/to-be-cleaned/Table' +import { useRouter } from 'next/router' +import { LogSqlSnippets } from 'types' + +export const LogsSavedPage: NextPage = () => { + const { content, ui } = useStore() + const router = useRouter() + const { ref } = router.query + + useEffect(()=>{ + content.loadPersistentData() + }, [ui.selectedProjectRef]) + const recent = content.recentLogSqlSnippets.slice().reverse() + + return ( + +
+ {recent.length > 0 && ( + <> +
+ +
+ + Snippets + + + } + body={ + <> + {recent.map((item: LogSqlSnippets.Content) => )} + + } + /> + + )} + + {recent.length === 0 && ( + <> +
+ +

No Recent Queries Yet

+

+ Your recent queries run from the{' '} + + Query + {' '} + tab will show here. +

+
+ + )} + + ) +} + +export default withAuth(observer(LogsSavedPage)) diff --git a/studio/stores/content/ProjectContentStore.ts b/studio/stores/content/ProjectContentStore.ts index ded20c86ac0..58b197cccec 100644 --- a/studio/stores/content/ProjectContentStore.ts +++ b/studio/stores/content/ProjectContentStore.ts @@ -1,8 +1,8 @@ -import { makeAutoObservable, action } from 'mobx' +import { makeAutoObservable } from 'mobx' import { keyBy } from 'lodash' import { get, post, patch, delete_ } from 'lib/common/fetch' -import { UserContent, UserContentMap } from 'types' +import { LogSqlSnippets, UserContent, UserContentMap } from 'types' import { IRootStore } from '../RootStore' import { API_URL } from 'lib/constants' @@ -15,16 +15,20 @@ export interface IProjectContentStore { isInitialized: boolean isLoaded: boolean error: any + recentLogSqlSnippets: LogSqlSnippets.Content[] baseUrl: string projectRef?: string load: () => void + loadPersistentData: () => Promise create: (x: UserContent) => { data: UserContent; error: { error: { message: string } } } list: (filter?: any) => any[] reports: (filter?: any) => any[] sqlSnippets: (filter?: any) => any[] logSqlSnippets: (filter?: CustomFilter) => UserContent[] + addRecentLogSqlSnippet: (snippet: Partial) => void + clearRecentLogSqlSnippets: () => void setProjectRef: (ref?: string) => void } @@ -39,9 +43,12 @@ export default class ProjectContentStore implements IProjectContentStore { } baseUrl: string + localStorageKey: string + recentLogSqlKey: string projectRef: string data: UserContentMap = {} + recentLogSqlSnippets: LogSqlSnippets.Content[] = [] state = this.STATES.INITIAL error = null @@ -50,6 +57,9 @@ export default class ProjectContentStore implements IProjectContentStore { const { projectRef } = options this.projectRef = projectRef this.rootStore = rootStore + this.localStorageKey = `project-content-${projectRef}` + this.recentLogSqlKey = `${this.localStorageKey}-recent-log-sql` + this.loadPersistentData() this.baseUrl = `` makeAutoObservable(this) } @@ -93,12 +103,21 @@ export default class ProjectContentStore implements IProjectContentStore { } } - async loadPersistentData() {} + async loadPersistentData() { + this.loadRecentLogSqlSnippets() + } async loadRemotePersistentData(userId: any) { const sqlSnippets = this.sqlSnippets((x: any) => x.owner_id === userId) } + loadRecentLogSqlSnippets() { + if (typeof window === 'undefined') return + this.recentLogSqlSnippets = JSON.parse( + (window as any).localStorage.getItem(this.recentLogSqlKey) || '[]' + ) + } + list(filter?: any) { const arr = Object.values(this.data) if (!!filter) { @@ -141,6 +160,25 @@ export default class ProjectContentStore implements IProjectContentStore { return arr_filtered } } + public addRecentLogSqlSnippet(snippet: Partial) { + if (typeof window === 'undefined') return + const defaults: LogSqlSnippets.Content = { + schema_version: '1', + favorite: false, + sql: '', + content_id: '', + } + this.recentLogSqlSnippets.push({ ...defaults, ...snippet }) + ;(window as any).localStorage.setItem( + this.recentLogSqlKey, + JSON.stringify(this.recentLogSqlSnippets) + ) + } + public clearRecentLogSqlSnippets() { + if (typeof window === 'undefined') return + this.recentLogSqlSnippets = [] + ;(window as any).localStorage.setItem(this.recentLogSqlKey, JSON.stringify([])) + } logSqlSnippets(filter?: CustomFilter) { let arr = ((Object.values(this.data)[0] as any) || []) as UserContent[] diff --git a/studio/types/userContent.ts b/studio/types/userContent.ts index 1219ea809b3..f7e9e755d7e 100644 --- a/studio/types/userContent.ts +++ b/studio/types/userContent.ts @@ -1,10 +1,10 @@ -export interface UserContent { +export interface UserContent { id?: string name: string description?: string type: 'sql' | 'report' | 'log_sql' visibility: 'user' | 'project' | 'org' | 'public' - content: Dashboards.Content | SqlSnippets.Content | LogSqlSnippets.Content + content: T owner_id?: number // user id last_updated_by?: number // user id inserted_at?: string // '2021-08-26T08:24:52.040695+00:00'