Skip to content

Commit

Permalink
wip: add lenses scoped
Browse files Browse the repository at this point in the history
  • Loading branch information
katallaxie committed Jan 22, 2024
1 parent 188f382 commit 405b211
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 80 deletions.
21 changes: 12 additions & 9 deletions packages/app/src/app/teams/[team]/lenses/new/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { SubNav, SubNavTitle, SubNavSubtitle } from '@/components/sub-nav'
import { SubNav, SubNavTitle } from '@/components/sub-nav'
import { Section } from '@/components/section'
import { NewLensForm } from '@/components/lenses/new-form'
import { type PropsWithChildren } from 'react'

export default function Page() {
export interface NextPageProps<TeamSlug = string> {
params: { team: TeamSlug }
searchParams?: { [key: string]: string | string[] | undefined }
}

export const revalidate = 0 // no cache

export default function Page(props: PropsWithChildren<NextPageProps>) {
return (
<>
<SubNav>
<SubNavTitle>
New Lens
<SubNavSubtitle>
Measure workloads against best practices.
</SubNavSubtitle>
</SubNavTitle>
<SubNavTitle>New Lens</SubNavTitle>
</SubNav>
<Section>
<NewLensForm />
<NewLensForm teamSlug={props.params.team} />
</Section>
</>
)
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/app/teams/[team]/lenses/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default async function Page(props: PropsWithChildren<NextPageProps>) {
</SubNavSubtitle>
</SubNavTitle>
<SubNavActions>
<AddLensButton />
<AddLensButton teamSlug={searchParams.slug} />
</SubNavActions>
</SubNav>
<Main className="space-y-8 p-8">
Expand Down
16 changes: 8 additions & 8 deletions packages/app/src/app/teams/[team]/solutions/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ import {
CardTitle,
CardDescription
} from '@/components/ui/card'
import DateFormat from '@/components/date-format'
import { Section } from '@/components/section'
import { ActionsMenu } from './components/actions-menu'
import { api } from '@/trpc/server-http'
import { CommentForm } from './components/comment-form'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import { CommentActions } from './components/comment-actions'
import { CommentForm } from './components/comment-form'
import { remark } from 'remark'
import { Section } from '@/components/section'
import DateFormat from '@/components/date-format'
import html from 'remark-html'
import Markdown from 'react-markdown'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import { ActionsMenu } from './components/actions-menu'

export type PageProps = {
params: { id: string }
Expand Down Expand Up @@ -56,7 +56,7 @@ export default async function Page({ params }: PageProps) {
src={solution?.user?.image}
alt={solution?.user?.name}
/>
<AvatarFallback>{}</AvatarFallback>
<AvatarFallback>{ }</AvatarFallback>
</Avatar>
</Button>
</CardTitle>
Expand Down Expand Up @@ -116,7 +116,7 @@ export default async function Page({ params }: PageProps) {
src={comment.user?.image}
alt={comment.user?.name}
/>
<AvatarFallback>{}</AvatarFallback>
<AvatarFallback>{ }</AvatarFallback>
</Avatar>
</Button>
<CommentActions comment={comment} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import { createAction, protectedProcedure } from '@/server/trpc'
import { rhfActionSchema } from './new-form.schema'
import { createProfile } from '@/db/services/profiles'
import { isAllowed } from '@/server/trpc'
import { revalidatePath } from 'next/cache'

export const rhfAction = createAction(
protectedProcedure
.use(isAllowed('write'))
.input(rhfActionSchema)
.mutation(
async opts =>
await createProfile({
...opts.input,
scope: { resourceType: 'profile', ownerId: opts.ctx.meta.ownerId }
})
)
.mutation(async opts => {
revalidatePath('/teams/[id]/lenses', 'page')
return await createProfile({
...opts.input,
scope: { resourceType: 'profile', ownerId: opts.ctx.meta.ownerId }
})
})
)
14 changes: 7 additions & 7 deletions packages/app/src/components/lenses/add-button.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Button } from '@/components/ui/button'
import { PlusIcon } from '@radix-ui/react-icons'
import Link from 'next/link'
import { PropsWithChildren } from 'react'

interface AddLensButtonProps {}
export interface AddLensButton {
teamSlug: string
}

export function AddLensButton({}: AddLensButtonProps) {
export function AddLensButton({ teamSlug }: PropsWithChildren<AddLensButton>) {
return (
<Link href="/dashboard/lenses/new" passHref>
<Button variant={'outline'}>
<PlusIcon />
</Button>
<Link href={`/teams/${teamSlug}/lenses/new`} passHref>
<Button variant={'outline'}>Add Lens</Button>
</Link>
)
}
22 changes: 14 additions & 8 deletions packages/app/src/components/lenses/new-form.action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ import 'server-only'
import { createAction, protectedProcedure } from '@/server/trpc'
import { rhfActionSchema } from './new-form.schema'
import { createLens } from '@/db/services/lenses'
import { isAllowed } from '@/server/trpc'

export const rhfAction = createAction(
protectedProcedure.input(rhfActionSchema).mutation(
async opts =>
await createLens({
name: opts.input.name,
description: opts.input.description,
spec: opts.input.spec
})
)
protectedProcedure
.use(isAllowed('write'))
.input(rhfActionSchema)
.mutation(
async opts =>
await createLens({
name: opts.input.name,
description: opts.input.description,
spec: opts.input.spec,
ownerId: opts.ctx.meta.ownerId,
resourceType: 'lens'
})
)
)
2 changes: 1 addition & 1 deletion packages/app/src/components/lenses/new-form.schema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const readJSONFile = async (file: File) =>
})

export const rhfActionSchema = z.object({
name: z.string().min(3).max(256).default(''),
name: z.string().trim().min(3).max(256).default(''),
spec: z
.union([
z
Expand Down
11 changes: 8 additions & 3 deletions packages/app/src/components/lenses/new-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ import { z } from 'zod'
import { useAction } from '@/trpc/client'
import { useRouter } from 'next/navigation'

export type NewLensFormProps = {}
export type NewLensFormProps = {
teamSlug: string
}

export function NewLensForm({ ...props }: PropsWithChildren<NewLensFormProps>) {
export function NewLensForm({
teamSlug,
...props
}: PropsWithChildren<NewLensFormProps>) {
const form = useForm<z.infer<typeof rhfActionSchema>>({
resolver: zodResolver(rhfActionSchema),
defaultValues: {
Expand All @@ -47,7 +52,7 @@ export function NewLensForm({ ...props }: PropsWithChildren<NewLensFormProps>) {

useEffect(() => {
if (mutation.status === 'success') {
router.push(`/dashboard/lenses/${mutation.data?.id}`)
router.push(`/teams/${teamSlug}/profiles/${mutation.data?.id}`)
}
}, [mutation.status, mutation.data?.id, router])

Expand Down
5 changes: 3 additions & 2 deletions packages/app/src/db/models/lens-pillar-risks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ForeignKey
} from 'sequelize-typescript'
import { LensPillarQuestion } from './lens-pillar-questions'
import { Risk } from '../schemas/spec'

export enum QuestionRisk {
Unanswered = 'UNANSWERED',
Expand Down Expand Up @@ -58,8 +59,8 @@ export class LensPillarQuestionRisk extends Model<

@NotEmpty
@Default(QuestionRisk.Unanswered)
@Column(DataType.ENUM(...Object.values(QuestionRisk)))
risk!: QuestionRisk
@Column(DataType.ENUM(...Object.values(Risk)))
risk!: Risk

@NotEmpty
@Min(3)
Expand Down
44 changes: 22 additions & 22 deletions packages/app/src/db/models/lens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ export interface LensAttributes {
isDraft: boolean
description?: string
pillars?: LensPillar[]
tags: Tag[]
teams: Team[]
createdAt: Date
updatedAt: Date
deletedAt: Date
tags?: Tag[]
teams?: Team[]
createdAt?: Date
updatedAt?: Date
deletedAt?: Date
}

export type LensCreationAttributes = Omit<
Expand All @@ -51,34 +51,34 @@ export class Lens extends Model<LensAttributes, LensCreationAttributes> {
@AllowNull(false)
@Default(DataType.UUIDV4)
@Column(DataType.UUIDV4)
id!: string
declare id: string

@NotEmpty
@Min(3)
@Max(256)
@Column
name!: string
@Column(DataType.STRING)
declare name: string

@NotEmpty
@Column
version!: string
@Column(DataType.STRING)
declare version: string

@NotEmpty
@Column(DataType.JSONB)
spec!: Spec
declare spec: Spec

@Default(true)
@Column
isDraft?: boolean
@Column(DataType.BOOLEAN)
declare isDraft: boolean

@NotEmpty
@Min(12)
@Max(2048)
@Column
description?: string
@Column(DataType.STRING)
declare description?: string

@HasMany(() => LensPillar, 'lensId')
pillars?: LensPillar[]
declare pillars?: LensPillar[]

@BelongsToMany(() => Tag, {
through: {
Expand Down Expand Up @@ -109,14 +109,14 @@ export class Lens extends Model<LensAttributes, LensCreationAttributes> {
declare teams: Team[]

@CreatedAt
@Column
createdAt?: Date
@Column(DataType.DATE)
declare createdAt: Date

@UpdatedAt
@Column
updatedAt?: Date
@Column(DataType.DATE)
declare updatedAt: Date

@DeletedAt
@Column
deletedAt?: Date
@Column(DataType.DATE)
declare deletedAt: Date
}
15 changes: 10 additions & 5 deletions packages/app/src/db/schemas/lenses.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { z } from 'zod'
import { FindOneTeamByNameSlug } from './teams'
import { PaginationSchema } from './pagination'
import { ScopeSchema } from './scope'

export const LensesGetSchema = z.string().uuid()
export const LensesDeleteSchema = z.string().uuid()
export const LensesPublishSchema = z.string().uuid()
export const LensesGetQuestionSchema = z.string()
export const LensesAddSchema = z.object({
name: z.string().min(1).max(256),
description: z.string().min(10).max(2048),
spec: z.string()
})

export const LensesAddSchema = z
.object({
name: z.string().min(1).max(256),
description: z.string().min(10).max(2048),
spec: z.string()
})
.and(ScopeSchema)
export type CreateLens = z.infer<typeof LensesAddSchema>

export const ListLensByTeamSlug = FindOneTeamByNameSlug.and(PaginationSchema)
export type ListLensByTeamSlug = z.infer<typeof ListLensByTeamSlug>
1 change: 0 additions & 1 deletion packages/app/src/db/schemas/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export const Risk = z.object({
]),
condition: z.string()
})

export type Risk = z.infer<typeof Risk>

export const Improvement = z.object({
Expand Down
24 changes: 18 additions & 6 deletions packages/app/src/db/services/lenses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ import {
LensesGetSchema,
LensesDeleteSchema,
LensesPublishSchema,
LensesGetQuestionSchema,
LensesAddSchema
LensesGetQuestionSchema
} from '../schemas/lenses'
import { Team } from '../models/teams'
import sequelize from '@/db/config/config'
import { z } from 'zod'
import { LensPillarResource } from '../models/lens-pillar-resources'
import { LensPillarQuestionRisk } from '../models/lens-pillar-risks'
import { ListLensByTeamSlug } from '../schemas/lenses'
import {
LensPillarQuestionRisk,
QuestionRisk
} from '../models/lens-pillar-risks'
import { ListLensByTeamSlug, CreateLens } from '../schemas/lenses'
import { Ownership } from '../models/ownership'

export type Pagination = {
offset?: number
Expand Down Expand Up @@ -47,7 +50,7 @@ export const deleteLens = async (opts: z.infer<typeof LensesDeleteSchema>) =>
export const publishLens = async (opts: z.infer<typeof LensesPublishSchema>) =>
await Lens.update({ isDraft: false }, { where: { id: opts } })

export const createLens = async (opts: z.infer<typeof LensesAddSchema>) =>
export const createLens = async (opts: CreateLens) =>
await sequelize.transaction(async transaction => {
const spec = await Spec.parseAsync(JSON.parse(opts.spec))

Expand All @@ -63,6 +66,15 @@ export const createLens = async (opts: z.infer<typeof LensesAddSchema>) =>
}
)

const ownership = await Ownership.create(
{
...opts,
resourceId: lens.id,
resourceType: 'lens'
},
{ transaction }
)

const pillars = await LensPillar.bulkCreate(
[
...spec.pillars.map(pillar => ({
Expand Down Expand Up @@ -112,7 +124,7 @@ export const createLens = async (opts: z.infer<typeof LensesAddSchema>) =>
...(spec.pillars[a].questions[b]?.risks?.map(risk => {
return {
questionId: question.id,
risk: risk.risk,
risk: QuestionRisk[risk.risk as keyof typeof QuestionRisk],
condition: risk.condition
}
}) ?? [])
Expand Down

0 comments on commit 405b211

Please sign in to comment.