Skip to content
This repository has been archived by the owner on May 11, 2021. It is now read-only.

Commit

Permalink
feat(manager): add form to creating custom tip
Browse files Browse the repository at this point in the history
  • Loading branch information
igorkamyshev committed Apr 17, 2019
1 parent e69b302 commit 35b70e7
Show file tree
Hide file tree
Showing 29 changed files with 417 additions and 9 deletions.
15 changes: 15 additions & 0 deletions back/evolutions/9.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CREATE TABLE public.custom_tip (
"id" character varying NOT NULL,
"title" character varying NOT NULL,
"text" character varying NOT NULL,
"link" character varying,
"expireAt" timestamp with time zone NOT NULL,
"important" BOOLEAN NOT NULL DEFAULT FALSE
);

ALTER TABLE ONLY public.custom_tip
ADD CONSTRAINT "PK_custom_tip_id" PRIMARY KEY ("id");

#DOWN

DROP TABLE public.custom_tip;
25 changes: 25 additions & 0 deletions back/src/mind/application/TipsCreator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable } from '@nestjs/common'

import { EntitySaver } from '@back/db/EntitySaver'
import { CustomTipModel } from '@shared/models/mind/CustomTipModel'
import { IdGenerator } from '@back/utils/infrastructure/IdGenerator/IdGenerator'

import { DisabledTip } from '../domain/DisabledTip.entity'
import { CustomTip } from '../domain/CustomTip.entity'

@Injectable()
export class TipsCreator {
public constructor(
private readonly entitySaver: EntitySaver,
private readonly idGenerator: IdGenerator,
) {}

public async createCustom(fields: CustomTipModel): Promise<void> {
const id = await this.idGenerator.getId()
const { title, text, expireAt, important, link } = fields

const tip = new CustomTip(id, title, text, expireAt, important, link)

await this.entitySaver.save(tip)
}
}
40 changes: 40 additions & 0 deletions back/src/mind/domain/CustomTip.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Column, Entity, PrimaryColumn } from 'typeorm'

import { CustomTipModel } from '@shared/models/mind/CustomTipModel'

@Entity()
export class CustomTip implements CustomTipModel {
@PrimaryColumn()
public readonly id: string

@Column()
public readonly title: string

@Column()
public readonly text: string

@Column()
public readonly link?: string

@Column()
public readonly expireAt: Date

@Column()
public readonly important: boolean

public constructor(
id: string,
title: string,
text: string,
expireAt: Date,
important: boolean = false,
link?: string,
) {
this.id = id
this.title = title
this.text = text
this.expireAt = expireAt
this.important = important
this.link = link
}
}
2 changes: 2 additions & 0 deletions back/src/mind/mind.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { TypoController } from './presentation/http/controller/TypoController'
import { TypoMerger } from './application/TypoMerger'
import { BudgetAdviser } from './application/adviser/BudgetAdviser'
import { ExtraSpendingAdviser } from './application/adviser/ExtraSpendingAdviser'
import { TipsCreator } from './application/TipsCreator'

@Module({
imports: [
Expand All @@ -36,6 +37,7 @@ import { ExtraSpendingAdviser } from './application/adviser/ExtraSpendingAdviser
AdviserUnity,
TipsFilter,
TipsDisabler,
TipsCreator,
DisabledTipRepository,
],
})
Expand Down
12 changes: 12 additions & 0 deletions back/src/mind/presentation/http/controller/TipController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import {
ApiOkResponse,
} from '@nestjs/swagger'

import { OnlyForManager } from '@back/user/presentation/http/security/OnlyForManager'
import { OnlyForUsers } from '@back/user/presentation/http/security/OnlyForUsers'
import { TokenPayloadModel } from '@shared/models/user/TokenPayloadModel'
import { CurrentUser } from '@back/user/presentation/http/decorator/CurrentUser'
import { AdviserUnity } from '@back/mind/infrastructure/adviser/AdviserUnity'
import { TipsFilter } from '@back/mind/application/TipsFilter'
import { TipsDisabler } from '@back/mind/application/TipsDisabler'
import { TipsCreator } from '@back/mind/application/TipsCreator'

import { TipResponse } from '../reponse/TipResponse'
import { DisableTipRequest } from '../request/DisableTipRequest'
import { CustomTipRequest } from '../request/CustomTipRequest'

@Controller('mind/tip')
@OnlyForUsers()
Expand All @@ -25,6 +28,7 @@ export class TipController {
private readonly adviser: AdviserUnity,
private readonly tipsFilter: TipsFilter,
private readonly tipsDisabler: TipsDisabler,
private readonly tipsCreator: TipsCreator,
) {}

@Get()
Expand Down Expand Up @@ -54,4 +58,12 @@ export class TipController {
) {
await this.tipsDisabler.disable(request.tokens, user.login)
}

@Post('create')
@OnlyForManager()
@ApiOperation({ title: 'Create custom tip' })
@ApiOkResponse({ description: 'Created' })
public async create(@Body() request: CustomTipRequest) {
await this.tipsCreator.createCustom(request)
}
}
20 changes: 20 additions & 0 deletions back/src/mind/presentation/http/request/CustomTipRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ApiModelProperty } from '@nestjs/swagger'

import { CustomTipModel } from '@shared/models/mind/CustomTipModel'

export class CustomTipRequest implements CustomTipModel {
@ApiModelProperty({ example: 'Alert' })
public readonly title: string

@ApiModelProperty({ example: 'All cool' })
public readonly text: string

@ApiModelProperty({ example: 'https://google.com', required: false })
public readonly link?: string

@ApiModelProperty({ example: new Date() })
public readonly expireAt: Date

@ApiModelProperty({ example: true })
public readonly important: boolean
}
2 changes: 1 addition & 1 deletion back/src/user/presentation/http/security/OnlyForManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import { UseGuards } from '@nestjs/common'

import { JwtManagerGuard } from './JwtManagerGuard'

export const OnlyForUsers = () => UseGuards(JwtManagerGuard)
export const OnlyForManager = () => UseGuards(JwtManagerGuard)
4 changes: 3 additions & 1 deletion front/pages/internal/manager.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as React from 'react'

import { Manager } from '@front/features/manager'

export default class HisotryPage extends React.Component {
public static isSecure = true

public render() {
return <p>MANAGER</p>
return <Manager />
}
}
10 changes: 10 additions & 0 deletions front/src/domain/mind/actions/createTip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { fetchOrFail } from '@front/domain/store'
import { CustomTipModel } from '@shared/models/mind/CustomTipModel'

import { createTipRequest } from '../api/createTipRequest'
import { actions as tipFetchingActions } from '../reducer/createTipFetching'

export const createTip = (tip: CustomTipModel) =>
fetchOrFail(tipFetchingActions, async (_, getApi) => {
await createTipRequest(getApi())(tip)
})
7 changes: 7 additions & 0 deletions front/src/domain/mind/api/createTipRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Api } from '@front/domain/api'
import { CustomTipModel } from '@shared/models/mind/CustomTipModel'

export const createTipRequest = (api: Api) => (
tip: CustomTipModel,
): Promise<void> =>
api.client.post('/mind/tip/create', tip).then(response => response.data)
7 changes: 7 additions & 0 deletions front/src/domain/mind/reducer/createTipFetching.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createFetchingRedux, FetchingState } from 'redux-clear'

type State = FetchingState

const { reducer, actions } = createFetchingRedux('manager/create-tip-fetching')

export { reducer, actions, State }
6 changes: 6 additions & 0 deletions front/src/domain/mind/reducer/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { combineReducers } from 'redux'

import { reducer as tipsReducer, State as TipsState } from './tips'
import {
reducer as createTipFetchingReducer,
State as CreateTipTipState,
} from './createTipFetching'

interface State {
tips: TipsState
createTipFetching: CreateTipTipState
}

const reducer = combineReducers<State>({
tips: tipsReducer,
createTipFetching: createTipFetchingReducer,
})

export { reducer, State }
4 changes: 4 additions & 0 deletions front/src/domain/mind/selectors/getCreateTipFetching.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { State } from '@front/domain/store'

export const getCreateTipFetching = (state: State) =>
state.mind.createTipFetching
23 changes: 23 additions & 0 deletions front/src/features/final-form/components/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Field, FieldRenderProps } from 'react-final-form'
import { Diff } from 'utility-types'

import {
Checkbox as JustCheckbox,
CheckboxProps,
} from '@front/ui/components/form/checkbox'

interface OwnProps {
name: string
}

type ComponentProps = Diff<CheckboxProps, FieldRenderProps['input']>

export const Checkbox = ({
name,
...componentProps
}: OwnProps & ComponentProps) => (
<Field
name={name}
render={({ input }) => <JustCheckbox {...componentProps} {...input} />}
/>
)
23 changes: 23 additions & 0 deletions front/src/features/final-form/components/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Field, FieldRenderProps } from 'react-final-form'
import { Diff } from 'utility-types'

import {
TextArea as JustTextArea,
TextAreaProps,
} from '@front/ui/components/form/text-area'

interface OwnProps {
name: string
}

type ComponentProps = Diff<TextAreaProps, FieldRenderProps['input']>

export const TextArea = ({
name,
...componentProps
}: OwnProps & ComponentProps) => (
<Field
name={name}
render={({ input }) => <JustTextArea {...componentProps} {...input} />}
/>
)
2 changes: 2 additions & 0 deletions front/src/features/final-form/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export { AutoComplete } from './components/AutoComplete'
export { Checkbox } from './components/Checkbox'
export { Input } from './components/Input'
export { InputMoney } from './components/InputMoney'
export { Select } from './components/Select'
export { EnumSelect } from './components/EnumSelect'
export { DatePicker } from './components/DatePicker'
export { TextArea } from './components/TextArea'
export { Toggle } from './components/Toggle'
11 changes: 11 additions & 0 deletions front/src/features/manager/Manager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Container } from '@front/ui/components/layout/container'

import { CreateTip } from './features/create-tip'

export const Manager = () => {
return (
<Container>
<CreateTip />
</Container>
)
}
48 changes: 48 additions & 0 deletions front/src/features/manager/features/create-tip/CreateTip.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.form {
display: grid;

grid-template:
'title title '
'content content'
'link expire '
'important submit '
/ 1fr 1fr;

@media (max-width: 768px) {
grid-template:
'title'
'content'
'link'
'expire'
'important'
'submit';
}

align-items: end;
gap: 16px;

& > *:nth-child(1) {
grid-area: title;
}

& > *:nth-child(2) {
grid-area: content;
}

& > *:nth-child(3) {
grid-area: link;
}

& > *:nth-child(4) {
grid-area: expire;
}

& > *:nth-child(5) {
grid-area: important;
align-self: center;
}

& > *:nth-child(6) {
grid-area: submit;
}
}
Loading

0 comments on commit 35b70e7

Please sign in to comment.