-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
377 additions
and
7 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
apps/platform/db/migrations/20240914230319_add_resources.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
exports.up = async function(knex) { | ||
await knex.schema.createTable('resources', function(table) { | ||
table.increments() | ||
table.integer('project_id') | ||
.unsigned() | ||
.notNullable() | ||
.references('id') | ||
.inTable('projects') | ||
.onDelete('CASCADE') | ||
table.string('type') | ||
table.string('name') | ||
table.json('value') | ||
table.timestamp('created_at').defaultTo(knex.fn.now()) | ||
table.timestamp('updated_at').defaultTo(knex.fn.now()) | ||
}) | ||
} | ||
|
||
exports.down = async function(knex) { | ||
await knex.schema.dropTable('resources') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import Model, { ModelParams } from '../core/Model' | ||
|
||
export type ResourceType = 'font' | 'snippet' | ||
|
||
export default class Resource extends Model { | ||
project_id!: number | ||
type!: ResourceType | ||
name!: string | ||
value!: Record<string, any> | ||
|
||
static jsonAttributes = ['value'] | ||
} | ||
|
||
export type ResourceParams = Omit<Resource, ModelParams> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import Router from '@koa/router' | ||
import { ProjectState } from '../auth/AuthMiddleware' | ||
import { JSONSchemaType, validate } from '../core/validate' | ||
import Resource, { ResourceParams, ResourceType } from './Resource' | ||
import { allResources, createResource, deleteResource, getResource } from './ResourceService' | ||
|
||
const router = new Router< | ||
ProjectState & { resource?: Resource } | ||
>({ | ||
prefix: '/resources', | ||
}) | ||
|
||
router.get('/', async ctx => { | ||
const type = ctx.query.type as ResourceType | ||
ctx.body = await allResources(ctx.state.project.id, type) | ||
}) | ||
|
||
const resourceCreateParams: JSONSchemaType<ResourceParams> = { | ||
$id: 'resourceCreateParams', | ||
type: 'object', | ||
required: ['type', 'name', 'value'], | ||
properties: { | ||
type: { | ||
type: 'string', | ||
enum: ['font', 'snippet'], | ||
}, | ||
name: { type: 'string' }, | ||
value: { | ||
type: 'object', | ||
additionalProperties: true, | ||
} as any, | ||
}, | ||
additionalProperties: false, | ||
} | ||
router.post('/', async ctx => { | ||
const payload = validate(resourceCreateParams, ctx.request.body) | ||
ctx.body = await createResource(ctx.state.project.id, payload) | ||
}) | ||
|
||
router.param('resourceId', async (value: string, ctx, next) => { | ||
ctx.state.resource = await getResource(parseInt(value, 10), ctx.state.project.id) | ||
if (!ctx.state.resource) { | ||
ctx.throw(404) | ||
return | ||
} | ||
return await next() | ||
}) | ||
|
||
router.delete('/:resourceId', async ctx => { | ||
const { id, project_id } = ctx.state.resource! | ||
await deleteResource(id, project_id) | ||
ctx.body = true | ||
}) | ||
|
||
export default router |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import Resource, { ResourceParams, ResourceType } from './Resource' | ||
|
||
export const allResources = async (projectId: number, type?: ResourceType): Promise<Resource[]> => { | ||
return await Resource.all(qb => { | ||
if (type) { | ||
qb.where('type', type) | ||
} | ||
return qb.where('project_id', projectId) | ||
}) | ||
} | ||
|
||
export const getResource = async (id: number, projectId: number) => { | ||
return await Resource.find(id, qb => qb.where('project_id', projectId)) | ||
} | ||
|
||
export const createResource = async (projectId: number, params: ResourceParams) => { | ||
return await Resource.insertAndFetch({ | ||
...params, | ||
project_id: projectId, | ||
}) | ||
} | ||
|
||
export const deleteResource = async (id: number, projectId: number) => { | ||
return await Resource.deleteById(id, qb => qb.where('project_id', projectId)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import Modal, { ModalStateProps } from '../../ui/Modal' | ||
import './ImageGalleryModal.css' | ||
import { Font, Resource } from '../../types' | ||
import { useTranslation } from 'react-i18next' | ||
import FormWrapper from '../../ui/form/FormWrapper' | ||
import TextInput from '../../ui/form/TextInput' | ||
import api from '../../api' | ||
import { useContext } from 'react' | ||
import { ProjectContext } from '../../contexts' | ||
|
||
interface ResourceModalProps extends ModalStateProps { | ||
onInsert?: (resource: Resource) => void | ||
} | ||
|
||
export default function ResourceFontModal({ open, onClose, onInsert }: ResourceModalProps) { | ||
const { t } = useTranslation() | ||
const [project] = useContext(ProjectContext) | ||
|
||
const handleCreateFont = async (params: Font) => { | ||
const resource = await api.resources.create(project.id, { | ||
type: 'font', | ||
name: params.name, | ||
value: params, | ||
}) | ||
onInsert?.(resource) | ||
} | ||
|
||
return ( | ||
<Modal | ||
title={t('add_font')} | ||
open={open} | ||
onClose={onClose} | ||
size="small"> | ||
<FormWrapper<Font> | ||
onSubmit={async (params) => { await handleCreateFont(params) }} | ||
submitLabel={t('create')}> | ||
{form => <> | ||
<TextInput.Field | ||
form={form} | ||
name="name" | ||
label={t('name')} | ||
required /> | ||
<TextInput.Field | ||
form={form} | ||
name="value" | ||
label="Font Family" | ||
required /> | ||
<TextInput.Field | ||
form={form} | ||
name="url" | ||
label="URL" | ||
required /> | ||
</>} | ||
</FormWrapper> | ||
</Modal> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import Modal, { ModalStateProps } from '../../ui/Modal' | ||
import { useContext, useState } from 'react' | ||
import { ProjectContext } from '../../contexts' | ||
import api from '../../api' | ||
import './ImageGalleryModal.css' | ||
import { Resource } from '../../types' | ||
import { Button, DataTable, Heading } from '../../ui' | ||
import { useTranslation } from 'react-i18next' | ||
import ResourceFontModal from './ResourceFontModal' | ||
|
||
interface ResourceModalProps extends ModalStateProps { | ||
resources: Resource[] | ||
setResources: (resources: Resource[]) => void | ||
} | ||
|
||
export default function ResourceModal({ open, onClose, resources, setResources }: ResourceModalProps) { | ||
const { t } = useTranslation() | ||
const [project] = useContext(ProjectContext) | ||
const [showFontCreate, setShowFontCreate] = useState(false) | ||
|
||
const handleRemove = async (id: number) => { | ||
await api.resources.delete(project.id, id) | ||
setResources(resources.filter(resource => resource.id !== id)) | ||
} | ||
|
||
const handleAddResource = (resource: Resource) => { | ||
setShowFontCreate(false) | ||
setResources([...resources, resource]) | ||
} | ||
|
||
return ( | ||
<Modal | ||
title="Config" | ||
open={open} | ||
onClose={onClose} | ||
size="large"> | ||
<Heading size="h4" title={t('fonts')} actions={ | ||
<Button size="small" onClick={() => setShowFontCreate(true)}>{t('add_font')}</Button> | ||
} /> | ||
<div className="resources"> | ||
<DataTable | ||
items={resources} | ||
itemKey={({ item }) => item.id} | ||
columns={[ | ||
{ | ||
key: 'name', | ||
title: t('name'), | ||
}, | ||
{ | ||
key: 'family', | ||
title: 'Font Family', | ||
cell: ({ item }) => item.value.value, | ||
}, | ||
{ | ||
key: 'url', | ||
title: 'URL', | ||
cell: ({ item }) => item.value.url, | ||
}, | ||
{ | ||
key: 'options', | ||
title: t('options'), | ||
cell: ({ item }) => ( | ||
<Button | ||
size="small" | ||
variant="destructive" | ||
onClick={async () => await handleRemove(item.id)}> | ||
{t('delete')} | ||
</Button> | ||
), | ||
}, | ||
]} /> | ||
</div> | ||
|
||
<ResourceFontModal | ||
open={showFontCreate} | ||
onClose={() => setShowFontCreate(false)} | ||
onInsert={(resource) => handleAddResource(resource) } | ||
/> | ||
</Modal> | ||
) | ||
} |
Oops, something went wrong.