Skip to content

Commit

Permalink
Merge pull request #1 from galadrimteam/folders
Browse files Browse the repository at this point in the history
Folders
  • Loading branch information
mle-moni committed May 27, 2024
2 parents bc1ce5a + 04a9868 commit af1a05d
Show file tree
Hide file tree
Showing 20 changed files with 557 additions and 201 deletions.
3 changes: 2 additions & 1 deletion app/adomin/adomin_config.types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { FolderViewConfig } from './create_folder_view_config.js'
import type { ModelConfig } from './create_model_view_config.js'
import type { StatsViewConfig } from './create_stats_view_config.js'

export type AdominViewConfig = ModelConfig | StatsViewConfig
export type AdominViewConfig = ModelConfig | StatsViewConfig | FolderViewConfig

export interface AdominConfig {
title: string
Expand Down
30 changes: 30 additions & 0 deletions app/adomin/api_views.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
interface ApiViewBase {
label: string
isHidden: boolean
fullPath: string
visibilityCheckPassed: boolean
name: string
/**
* Icon name, by default this uses Tabler icons
*
* You can browse the list of available icons at:
* https://tabler.io/icons
*/
icon?: string
}

export interface ApiModelView extends ApiViewBase {
type: 'model'
labelPluralized: string
}

export interface ApiStatView extends ApiViewBase {
type: 'stats'
}

export interface ApiFolderView extends ApiViewBase {
type: 'folder'
views: ApiAdominView[]
}

export type ApiAdominView = ApiModelView | ApiStatView | ApiFolderView
9 changes: 1 addition & 8 deletions app/adomin/config/adomin_config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import { AdominConfig } from '#adomin/adomin_config.types'
import {
IDEA_CONFIG,
PROFILE_CONFIG,
STATS_CONFIG,
TEST_CONFIG,
USER_CONFIG,
} from '../../test_adomin_config.js'

/**
* This file will contain your Adomin Config
Expand All @@ -25,5 +18,5 @@ import {

export const ADOMIN_CONFIG: AdominConfig = {
title: 'Adomin (edit this)',
views: [STATS_CONFIG, USER_CONFIG, TEST_CONFIG, PROFILE_CONFIG, IDEA_CONFIG],
views: [],
}
53 changes: 53 additions & 0 deletions app/adomin/create_folder_view_config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { AdominViewConfig } from './adomin_config.types.js'
import type { AdominRightsCheckFunction } from './routes/adomin_routes_overrides_and_rights.js'

export interface FolderViewConfig {
type: 'folder'
/**
* Title of the folder, displayed in the sidebar
*/
label: string
/**
* Used to determine the path in the frontend
*
* e.g. if name = 'test', full path on the frontend will be /adomin/folders/test
*/
name: string
/**
* Each object in the array represents a view in the folder (which can be a model, a folder or a stats view)
*/
views: AdominViewConfig[]
/** Check if logged in user can see this folder */
visibilityCheck?: AdominRightsCheckFunction
/**
* If true, the view will be hidden on the frontend (but still accessible if you know the path)
*
* if you want to restrict access to a view, use the `visibilityCheck` property
*/
isHidden?: boolean
/**
* Icon name, by default this uses Tabler icons
*
* You can browse the list of available icons at:
* https://tabler.io/icons
*/
icon?: string
}

export type FolderViewConfigStaticOptions = Omit<FolderViewConfig, 'type'>

export const createFolderViewConfig = (
options: FolderViewConfigStaticOptions
): FolderViewConfig => {
const { name, label, visibilityCheck, views, isHidden, icon } = options

return {
type: 'folder',
name,
label,
visibilityCheck,
views,
isHidden,
icon,
}
}
8 changes: 8 additions & 0 deletions app/adomin/create_model_view_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ export interface ModelConfigStaticOptions {
*
* frontend routes for create/update/list will still be created and available, but the navbar won't show it */
isHidden?: boolean
/**
* Icon name, by default this uses Tabler icons
*
* You can browse the list of available icons at:
* https://tabler.io/icons
*/
icon?: string
}

export interface ModelConfig extends ModelConfigStaticOptions {
Expand Down Expand Up @@ -185,5 +192,6 @@ export const createModelViewConfig = <T extends LucidModel>(
crudlRights: options.crudlRights,
visibilityCheck: options.visibilityCheck,
queryBuilderCallback: options.queryBuilderCallback,
icon: options.icon,
}
}
16 changes: 12 additions & 4 deletions app/adomin/create_stats_view_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export interface StatsViewConfig {
/**
* Path in the frontend
*
* e.g. if path = 'kpis', full path on the frontend will be /adomin/stats/kpis
* e.g. if name = 'kpis', full path on the frontend will be /adomin/stats/kpis
*/
path: string
name: string
/** Check if logged in user can see this view */
visibilityCheck?: AdominRightsCheckFunction
/**
Expand All @@ -24,6 +24,13 @@ export interface StatsViewConfig {
* if you want to restrict access to a view, use the `visibilityCheck` property
*/
isHidden?: boolean
/**
* Icon name, by default this uses Tabler icons
*
* You can browse the list of available icons at:
* https://tabler.io/icons
*/
icon?: string
}

type ChartDataRow = [string, number]
Expand Down Expand Up @@ -117,14 +124,15 @@ interface AdominStat {
export type StatsViewConfigStaticOptions = Omit<StatsViewConfig, 'type'>

export const createStatsViewConfig = (options: StatsViewConfigStaticOptions): StatsViewConfig => {
const { path, stats, label, visibilityCheck, isHidden } = options
const { name, stats, label, visibilityCheck, isHidden, icon } = options

return {
type: 'stats',
path,
name,
stats,
label,
visibilityCheck,
isHidden,
icon,
}
}
16 changes: 16 additions & 0 deletions app/adomin/get_flat_views.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { AdominViewConfig } from './adomin_config.types.js'
import { ADOMIN_CONFIG } from './config/adomin_config.js'

export const flattenViews = (views: AdominViewConfig[]): AdominViewConfig[] => {
const flatViews = views.flatMap((view) => {
if (view.type === 'folder') return flattenViews(view.views)

return view
})

return flatViews
}

export const getFlatViews = () => {
return flattenViews(ADOMIN_CONFIG.views)
}
18 changes: 9 additions & 9 deletions app/adomin/routes/adomin_router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ router
.group(() => {
router
.group(() => {
router.get('config', getAdominConfig)
router.get('config/models/:model', getModelConfigRoute)
router.get('config/stats/:view', getStatConfigRoute)

router.get('config', getAdominConfig)
router.get('config/:model', getModelConfigRoute)

router.post('crud/export/:model', modelList)
router.get('crud/:model', modelList)
router.get('crud/:model/:id', showModel)
router.put('crud/:model/:id', updateModel)
router.delete('crud/:model/:id', deleteModel)
router.post('crud/:model', createModel)
router.post('models/crud/export/:model', modelList)
router.get('models/crud/:model', modelList)
router.get('models/crud/:model/:id', showModel)
router.put('models/crud/:model/:id', updateModel)
router.delete('models/crud/:model/:id', deleteModel)
router.post('models/crud/:model', createModel)

router.post('logout', adominLogout)
})
.use(middleware.auth())
Expand Down
72 changes: 56 additions & 16 deletions app/adomin/routes/get_adomin_config.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,93 @@
import type { AdominViewConfig } from '#adomin/adomin_config.types'
import { ApiAdominView, ApiFolderView, ApiModelView, ApiStatView } from '#adomin/api_views.types'
import type { FolderViewConfig } from '#adomin/create_folder_view_config'
import type { StatsViewConfig } from '#adomin/create_stats_view_config'
import type { HttpContext } from '@adonisjs/core/http'
import { ADOMIN_CONFIG } from '../config/adomin_config.js'
import type { ModelConfig } from '../create_model_view_config.js'
import type { StatsViewConfig } from '../create_stats_view_config.js'
import { computeRightsCheck } from './adomin_routes_overrides_and_rights.js'

export const defaultFooterText = 'Made with ❤️ by Galadrim'

const getModelViewConfig = async (ctx: HttpContext, conf: ModelConfig) => {
const { label, labelPluralized, name, isHidden, visibilityCheck } = conf

const getModelViewConfig = async (ctx: HttpContext, conf: ModelConfig): Promise<ApiModelView> => {
const { label, labelPluralized, name, isHidden = false, visibilityCheck, icon } = conf
const visibilityCheckResult = await computeRightsCheck(ctx, visibilityCheck, false)
const fullPath = `/adomin/models/${name}`

return {
type: 'model',
label,
labelPluralized,
model: name,
isHidden: isHidden ?? false,
name,
isHidden,
visibilityCheckPassed: visibilityCheckResult === 'OK',
fullPath,
icon,
}
}

const getStatViewConfig = async (ctx: HttpContext, conf: StatsViewConfig) => {
const { path, label, visibilityCheck, isHidden } = conf

const getStatViewConfig = async (ctx: HttpContext, conf: StatsViewConfig): Promise<ApiStatView> => {
const { name, label, visibilityCheck, isHidden = false, icon } = conf
const visibilityCheckResult = await computeRightsCheck(ctx, visibilityCheck, false)
const fullPath = `/adomin/stats/${name}`

return {
type: 'stats',
label,
path,
isHidden: isHidden ?? false,
name,
isHidden,
visibilityCheckPassed: visibilityCheckResult === 'OK',
fullPath,
icon,
}
}

export const getAdominConfig = async (ctx: HttpContext) => {
const { auth } = ctx
const user = auth.user!
const viewsPromises = ADOMIN_CONFIG.views.map(async (conf) => {
const getFolderViewConfig = async (
ctx: HttpContext,
conf: FolderViewConfig
): Promise<ApiFolderView> => {
const { name, label, visibilityCheck, views, isHidden = false, icon } = conf

const fullPath = `/adomin/folders/${name}`
const visibilityCheckResult = await computeRightsCheck(ctx, visibilityCheck, false)
const finalViews = await getViewsConfig(ctx, views)

return {
type: 'folder',
label,
visibilityCheckPassed: visibilityCheckResult === 'OK',
views: finalViews,
isHidden,
fullPath,
name,
icon,
}
}

const getViewsConfig = async (
ctx: HttpContext,
views: AdominViewConfig[]
): Promise<ApiAdominView[]> => {
const viewsPromises = views.map(async (conf) => {
if (conf.type === 'stats') {
return getStatViewConfig(ctx, conf)
}
if (conf.type === 'folder') {
return getFolderViewConfig(ctx, conf)
}
return getModelViewConfig(ctx, conf)
})

const viewsToFilter = await Promise.all(viewsPromises)
const views = viewsToFilter.filter(({ visibilityCheckPassed }) => visibilityCheckPassed)
const filteredViews = viewsToFilter.filter(({ visibilityCheckPassed }) => visibilityCheckPassed)

return filteredViews
}

export const getAdominConfig = async (ctx: HttpContext) => {
const { auth } = ctx
const user = auth.user!
const views = await getViewsConfig(ctx, ADOMIN_CONFIG.views)
const footerText = ADOMIN_CONFIG.footerText ?? defaultFooterText

return {
Expand Down
6 changes: 3 additions & 3 deletions app/adomin/routes/models/get_model_config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getFlatViews } from '#adomin/get_flat_views'
import string from '@adonisjs/core/helpers/string'
import { HttpContext } from '@adonisjs/core/http'
import { AdominViewConfig } from '../../adomin_config.types.js'
import { ADOMIN_CONFIG } from '../../config/adomin_config.js'
import { ColumnConfig, ModelConfig } from '../../create_model_view_config.js'
import {
AdominStaticRightsConfig,
Expand Down Expand Up @@ -38,7 +38,7 @@ export const isModelConfig = (config: AdominViewConfig): config is ModelConfig =
}

export const getModelConfig = (modelName: string) => {
const foundConfig = ADOMIN_CONFIG.views
const foundConfig = getFlatViews()
.filter(isModelConfig)
.find((config) => config.model().name === modelName)

Expand All @@ -51,7 +51,7 @@ export const getModelConfigRoute = async (ctx: HttpContext) => {
const { params, response } = ctx
const modelString = params.model

const modelConfig = ADOMIN_CONFIG.views
const modelConfig = getFlatViews()
.filter(isModelConfig)
.find(({ model }) => model().name === modelString)

Expand Down
Loading

0 comments on commit af1a05d

Please sign in to comment.