-
+
{activeTab === 'archived'
- ? `No archived ${
- ['warning', 'critical'].includes(selectedFilter)
- ? `${selectedFilter} `
+ ? `No archived notifications${
+ snap.numFiltersApplied > 0
+ ? ` based on the ${snap.numFiltersApplied} filter${
+ snap.numFiltersApplied > 1 ? 's' : ''
+ } applied`
: ''
- }notifications`
+ }`
+ : snap.numFiltersApplied > 0
+ ? `No notifications based on the ${snap.numFiltersApplied} filter${
+ snap.numFiltersApplied > 1 ? 's' : ''
+ } applied`
: 'All caught up'}
-
+
{activeTab === 'archived'
? 'Notifications that you have previously archived will be shown here'
: 'You will be notified here for any notices on your organizations and projects'}
@@ -264,4 +239,4 @@ const NotificationsPopverV2 = () => {
)
}
-export default NotificationsPopverV2
+export default NotificationsPopoverV2
diff --git a/apps/studio/data/api.d.ts b/apps/studio/data/api.d.ts
index 85e1b46997a..e0e09be4a33 100644
--- a/apps/studio/data/api.d.ts
+++ b/apps/studio/data/api.d.ts
@@ -724,7 +724,7 @@ export interface paths {
get: operations['VercelAccessTokenController_getAccessToken']
}
'/platform/vercel/projects': {
- /** Gets the project with the given ID if provided, otherwise gets the list of projects */
+ /** Gets the list of Vercel projects */
get: operations['VercelProjectsController_getVercelProjects']
}
'/platform/vercel/projects/{id}': {
@@ -1610,6 +1610,10 @@ export interface paths {
/** Enables Database Webhooks on the project */
post: operations['V1DatabaseWebhooksController_v1EnableDatabaseWebhooks']
}
+ '/v1/projects/{ref}/database/backups': {
+ /** Lists all backups */
+ get: operations['V1BackupsController_getBackups']
+ }
'/v1/projects/{ref}/database/backups/restore-pitr': {
/** Restores a PITR backup for a database */
post: operations['V1RestorePitrController_v1RestorePitr']
@@ -1650,6 +1654,10 @@ export interface paths {
*/
get: operations['FunctionSlugController_getFunctionBody']
}
+ '/v1/projects/{ref}/storage/buckets': {
+ /** Lists all buckets */
+ get: operations['V1StorageBucketsController_getBuckets']
+ }
'/v1/organizations': {
/**
* List all organizations
@@ -4789,6 +4797,22 @@ export interface components {
created_at?: string
updated_at?: string
}
+ V1Backup: {
+ /** @enum {string} */
+ status: 'COMPLETED' | 'FAILED' | 'PENDING' | 'REMOVED' | 'ARCHIVED'
+ is_physical_backup: boolean
+ inserted_at: string
+ }
+ V1BackupsResponse: {
+ region: string
+ walg_enabled: boolean
+ pitr_enabled: boolean
+ backups: components['schemas']['V1Backup'][]
+ physical_backup_data: {
+ earliest_physical_backup_date_unix?: number
+ latest_physical_backup_date_unix?: number
+ }
+ }
V1RestorePitrBody: {
recovery_time_target_unix: number
}
@@ -4806,6 +4830,14 @@ export interface components {
entrypoint_path?: string
import_map_path?: string
}
+ V1StorageBucketResponse: {
+ id: string
+ name: string
+ owner: string
+ created_at: string
+ updated_at: string
+ public: boolean
+ }
OrganizationResponseV1: {
id: string
name: string
@@ -5040,6 +5072,8 @@ export interface operations {
query: {
status: 'new' | 'seen' | 'archived'
priority: 'Critical' | 'Warning' | 'Info'
+ org_slug: string[]
+ project_ref: string[]
offset: number
limit: number
}
@@ -10055,7 +10089,7 @@ export interface operations {
}
}
}
- /** Gets the project with the given ID if provided, otherwise gets the list of projects */
+ /** Gets the list of Vercel projects */
VercelProjectsController_getVercelProjects: {
parameters: {
query: {
@@ -10069,10 +10103,10 @@ export interface operations {
responses: {
200: {
content: {
- 'application/json': Record
+ 'application/json': Record[]
}
}
- /** @description Failed to get project(s) */
+ /** @description Failed to get projects */
500: {
content: never
}
@@ -12334,6 +12368,26 @@ export interface operations {
}
}
}
+ /** Lists all backups */
+ V1BackupsController_getBackups: {
+ parameters: {
+ path: {
+ /** @description Project ref */
+ ref: string
+ }
+ }
+ responses: {
+ 200: {
+ content: {
+ 'application/json': components['schemas']['V1BackupsResponse']
+ }
+ }
+ /** @description Failed to get backups */
+ 500: {
+ content: never
+ }
+ }
+ }
/** Restores a PITR backup for a database */
V1RestorePitrController_v1RestorePitr: {
parameters: {
@@ -12475,6 +12529,29 @@ export interface operations {
}
}
}
+ /** Lists all buckets */
+ V1StorageBucketsController_getBuckets: {
+ parameters: {
+ path: {
+ /** @description Project ref */
+ ref: string
+ }
+ }
+ responses: {
+ 200: {
+ content: {
+ 'application/json': components['schemas']['V1StorageBucketResponse'][]
+ }
+ }
+ 403: {
+ content: never
+ }
+ /** @description Failed to get list of buckets */
+ 500: {
+ content: never
+ }
+ }
+ }
/** Create an organization */
OrganizationsController_createOrganization: {
requestBody: {
diff --git a/apps/studio/data/notifications/keys.ts b/apps/studio/data/notifications/keys.ts
index 8aae9441f17..29945c796f3 100644
--- a/apps/studio/data/notifications/keys.ts
+++ b/apps/studio/data/notifications/keys.ts
@@ -1,6 +1,6 @@
export const notificationKeys = {
list: () => ['notifications'] as const,
- listV2: (options?: { status?: string; priority?: string; limit: number }) =>
+ listV2: (options?: { status?: string; limit: number; filters: any }) =>
['notifications', options] as const,
summary: () => ['notifications', 'summary'] as const,
}
diff --git a/apps/studio/data/notifications/notifications-v2-query.ts b/apps/studio/data/notifications/notifications-v2-query.ts
index d895bc5dfc6..9df9b8e5412 100644
--- a/apps/studio/data/notifications/notifications-v2-query.ts
+++ b/apps/studio/data/notifications/notifications-v2-query.ts
@@ -8,11 +8,14 @@ import { notificationKeys } from './keys'
const NOTIFICATIONS_PAGE_LIMIT = 10
export type NotificationVariables = {
- // archived: boolean
page: number
limit?: number
status?: 'new' | 'seen' | 'archived'
- priority?: 'Critical' | 'Warning' | 'Info'
+ filters: {
+ priority: readonly string[]
+ organizations: readonly string[]
+ projects: readonly string[]
+ }
}
export type Notification = components['schemas']['NotificationResponseV2']
@@ -33,7 +36,7 @@ export type NotificationData = {
}
export async function getNotifications(options: NotificationVariables, signal?: AbortSignal) {
- const { page = 0, limit = NOTIFICATIONS_PAGE_LIMIT, status, priority } = options
+ const { status, filters, page = 0, limit = NOTIFICATIONS_PAGE_LIMIT } = options
const { data, error } = await get('/platform/notifications', {
params: {
// @ts-ignore
@@ -41,7 +44,9 @@ export async function getNotifications(options: NotificationVariables, signal?:
offset: page * limit,
limit,
...(status !== undefined ? { status } : { status: ['new', 'seen'] }),
- ...(priority !== undefined ? { priority } : {}),
+ ...(filters.priority.length > 0 ? { priority: filters.priority } : {}),
+ ...(filters.organizations.length > 0 ? { org_slug: filters.organizations } : {}),
+ ...(filters.projects.length > 0 ? { project_ref: filters.projects } : {}),
},
},
headers: { Version: '2' },
@@ -57,16 +62,16 @@ export type NotificationsData = Awaited>
export type NotificationsError = ResponseError
export const useNotificationsV2Query = (
- { status, priority, limit = NOTIFICATIONS_PAGE_LIMIT }: Omit,
+ { status, filters, limit = NOTIFICATIONS_PAGE_LIMIT }: Omit,
{
enabled = true,
...options
}: UseInfiniteQueryOptions = {}
) => {
return useInfiniteQuery(
- notificationKeys.listV2({ status, priority, limit }),
+ notificationKeys.listV2({ status, filters, limit }),
({ signal, pageParam }) =>
- getNotifications({ status, priority, limit, page: pageParam }, signal),
+ getNotifications({ status, filters, limit, page: pageParam }, signal),
{
enabled: enabled,
getNextPageParam(lastPage, pages) {
diff --git a/apps/studio/state/notifications.ts b/apps/studio/state/notifications.ts
new file mode 100644
index 00000000000..4a1c0be56bb
--- /dev/null
+++ b/apps/studio/state/notifications.ts
@@ -0,0 +1,69 @@
+import { proxy, snapshot, useSnapshot } from 'valtio'
+
+export const notificationsState = proxy({
+ filterStatuses: [] as string[],
+ filterPriorities: [] as string[],
+ filterOrganizations: [] as string[],
+ filterProjects: [] as string[],
+ get numFiltersApplied() {
+ return [
+ ...this.filterStatuses,
+ ...this.filterPriorities,
+ ...this.filterOrganizations,
+ ...this.filterProjects,
+ ].length
+ },
+ setFilters: (value: string, type: 'status' | 'priority' | 'organizations' | 'projects') => {
+ switch (type) {
+ case 'status':
+ if (notificationsState.filterStatuses.includes(value)) {
+ notificationsState.filterStatuses = notificationsState.filterStatuses.filter(
+ (x) => x !== value
+ )
+ } else {
+ notificationsState.filterStatuses = notificationsState.filterStatuses.concat([value])
+ }
+ break
+ case 'priority':
+ if (notificationsState.filterPriorities.includes(value)) {
+ notificationsState.filterPriorities = notificationsState.filterPriorities.filter(
+ (x) => x !== value
+ )
+ } else {
+ notificationsState.filterPriorities = notificationsState.filterPriorities.concat([value])
+ }
+ break
+ case 'organizations':
+ if (notificationsState.filterOrganizations.includes(value)) {
+ notificationsState.filterOrganizations = notificationsState.filterOrganizations.filter(
+ (x) => x !== value
+ )
+ } else {
+ notificationsState.filterOrganizations = notificationsState.filterOrganizations.concat([
+ value,
+ ])
+ }
+ break
+ case 'projects':
+ if (notificationsState.filterProjects.includes(value)) {
+ notificationsState.filterProjects = notificationsState.filterProjects.filter(
+ (x) => x !== value
+ )
+ } else {
+ notificationsState.filterProjects = notificationsState.filterProjects.concat([value])
+ }
+ break
+ }
+ },
+ resetFilters: () => {
+ notificationsState.filterStatuses = []
+ notificationsState.filterPriorities = []
+ notificationsState.filterOrganizations = []
+ notificationsState.filterProjects = []
+ },
+})
+
+export const getNotificationsStateSnapshot = () => snapshot(notificationsState)
+
+export const useNotificationsStateSnapshot = (options?: Parameters[1]) =>
+ useSnapshot(notificationsState, options)