Skip to content

Commit

Permalink
Replace departments with custom roles
Browse files Browse the repository at this point in the history
  • Loading branch information
ba1uev committed Feb 8, 2024
1 parent 562ad87 commit 855d1d3
Show file tree
Hide file tree
Showing 42 changed files with 382 additions and 283 deletions.
8 changes: 0 additions & 8 deletions config-demo/company.json
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,5 @@
}
]
}
],
"departments": [
"Engineering",
"Operations",
"Finance",
"Legal",
"Security",
"Marketing"
]
}
5 changes: 0 additions & 5 deletions config-demo/modules.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@
"label": "Birthday",
"required": false
},
"department": {
"label": "Department",
"placeholder": "Select a department",
"required": true
},
"team": {
"label": "Team",
"placeholder": "Team name",
Expand Down
5 changes: 0 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ e.g
"label": "Birthday",
"required": false
},
"department": {
"label": "Department",
"placeholder": "Select a department",
"required": true,
},
"team": {
"label": "Team",
"placeholder": "Team name",
Expand Down
3 changes: 0 additions & 3 deletions scripts/client.build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ function getBuildConfig(): BuildOptions {
])
)
),
'process.env.DEPARTMENTS': JSON.stringify(
appConfig.config.company.departments
),
'process.env.LAYOUT': JSON.stringify(appConfig.config.application.layout),
'process.env.APP_NAME': JSON.stringify(appConfig.config.application.name),
'process.env.COMPANY_NAME': JSON.stringify(appConfig.config.company.name),
Expand Down
2 changes: 1 addition & 1 deletion src/client/components/ui/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type Props = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
const labeledTypes = ['checkbox', 'radio']
type LabelProps = {
name?: string | undefined
label: string | undefined
label: string | React.ReactNode | undefined
required?: boolean | undefined
type?: string | undefined
extraLabel?: string | null
Expand Down
4 changes: 1 addition & 3 deletions src/client/components/ui/UserLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import { USER_ROLE_BY_ID } from '#client/constants'

type Props = {
user: User | UserCompact
hideRole?: boolean
}
export const UserLabel: React.FC<Props> = ({ user, hideRole = false }) => {
export const UserLabel: React.FC<Props> = ({ user }) => {
return user ? (
<span className="inline-flex items-center w-max">
<Avatar
Expand All @@ -32,7 +31,6 @@ export const UserLabel: React.FC<Props> = ({ user, hideRole = false }) => {
>
{user.fullName}
</Link>
{!hideRole && <UserRoleLabel role={user.role} className="ml-2" />}
</span>
) : null
}
Expand Down
2 changes: 0 additions & 2 deletions src/client/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export type ClientUserRoleGroup = UserRoleGroup & {
type ClientAppConfig = {
modules: ClientModuleConfig[]
offices: ClientOfficeConfig[]
departments: string[]
appName: string
companyName: string
appHost: string
Expand All @@ -57,7 +56,6 @@ type ClientAuthConfig = { providers: string[] }
const config: ClientAppConfig = {
modules: process.env.APP_MODULES as unknown as ClientModuleConfig[],
offices: process.env.APP_OFFICES as unknown as ClientOfficeConfig[],
departments: process.env.DEPARTMENTS as unknown as string[],
appName: process.env.APP_NAME as unknown as string,
companyName: process.env.COMPANY_NAME as unknown as string,
appHost: process.env.APP_HOST as unknown as string,
Expand Down
6 changes: 1 addition & 5 deletions src/integrations/email-smtp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,7 @@ class EmailSMTP extends Integration {
})
}

public async sendEmail({
to,
html,
subject,
}: Email): Promise<SafeResponse<void>> {
public async sendEmail({ to, html, subject }: Email): Promise<SafeResponse> {
if (config.debug) {
console.log(
`Sending email skipped (debug mode).\nemail: ${to}\nsubject: ${subject}\nhtml: ${html}\n`
Expand Down
4 changes: 2 additions & 2 deletions src/integrations/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export class Integration {
return { success: false, error: error as Error }
}

success<T = void>(data?: T): SafeResponse<T> {
success<T>(data?: T): SafeResponse<T> {
if (data !== undefined) {
return { success: true, data: data } as SafeResponse<T>
}
return { success: true }
return { success: true } as SafeResponse<T>
}
}
11 changes: 3 additions & 8 deletions src/integrations/matrix/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class Matrix extends Integration {
public async inviteUserInRoom(
roomId: MatrixRoomId,
username: MatrixUsername
): Promise<SafeResponse<void>> {
): Promise<SafeResponse> {
const usernameFormatted = this.sanitizeUsername(username)
if (config.debug) {
console.log(
Expand Down Expand Up @@ -175,10 +175,7 @@ class Matrix extends Integration {
process.nextTick(() => this.inviteUserInRoom(roomId, username))
}

async sendMessageToUser(
user: User,
message: string
): Promise<SafeResponse<void>> {
async sendMessageToUser(user: User, message: string): Promise<SafeResponse> {
try {
const roomId = await this.ensureUserRoom(user)
if (roomId) {
Expand All @@ -194,9 +191,7 @@ class Matrix extends Integration {
process.nextTick(() => this.sendMessageToUser(user, message))
}

public async sendMessageInAdminRoom(
message: string
): Promise<SafeResponse<void>> {
public async sendMessageInAdminRoom(message: string): Promise<SafeResponse> {
try {
await this.sendMessageInRoom(this.credentials.adminRoomId, message)
return this.success()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const _AdminAnnouncements: React.FC<{}> = () => {
Header: 'Creator',
accessor: (one: any) => {
const user = usersById[one.creatorUserId]
return <UserLabel user={user} hideRole />
return <UserLabel user={user} />
},
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/modules/announcements/server/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
order: [['scheduledAt', 'ASC']],
where: {
visibility: EntityVisibility.Visible,
allowedRoles: { [Op.contains]: [req.user.role] },
allowedRoles: { [Op.overlap]: req.user.roles },
offices: { [Op.contains]: [req.office.id] },
scheduledAt: { [Op.lte]: today.toDate() },
expiresAt: { [Op.gt]: today.toDate() },
Expand Down
2 changes: 1 addition & 1 deletion src/modules/events/client/components/AdminEvents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export const AdminEvents = () => {
Header: 'Creator',
accessor: (event: EventAdminResponse) => {
const user = usersById[event.creatorUserId]
return <UserLabel user={user} hideRole />
return <UserLabel user={user} />
},
},
{
Expand Down
23 changes: 13 additions & 10 deletions src/modules/events/server/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
PublicForm,
} from '#modules/forms/types'
import { EntityVisibility } from '#shared/types'
import * as fp from '#shared/utils/fp'
import { Permissions } from '../permissions'
import {
Event,
Expand Down Expand Up @@ -58,8 +59,10 @@ const publicRouter: FastifyPluginCallback = async function (fastify, opts) {
if (event.visibility === EntityVisibility.None) {
return reply.throw.notFound()
}
const userRole = req.user ? req.user.role : appConfig.lowPriorityRole
if (!event.allowedRoles.includes(userRole)) {
const userRoles = req.user
? req.user.roles
: [appConfig.lowPriorityRole]
if (!fp.hasIntersection(event.allowedRoles, userRoles)) {
return reply.throw.notFound()
}
}
Expand Down Expand Up @@ -100,7 +103,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
if (event.visibility === EntityVisibility.None) {
return reply.throw.notFound()
}
if (!event.allowedRoles.includes(req.user.role)) {
if (!fp.hasIntersection(event.allowedRoles, req.user.roles)) {
return reply.throw.notFound()
}
}
Expand Down Expand Up @@ -169,7 +172,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
return reply.throw.notFound()
}
// Inherit access policy from the parent event
if (!event.allowedRoles.includes(req.user.role)) {
if (!fp.hasIntersection(event.allowedRoles, req.user.roles)) {
return reply.throw.notFound()
}
}
Expand Down Expand Up @@ -235,7 +238,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
return reply.throw.notFound()
}
// Inherit access policy from the parent event
if (!event.allowedRoles.includes(req.user.role)) {
if (!fp.hasIntersection(event.allowedRoles, req.user.roles)) {
return reply.throw.notFound()
}
}
Expand Down Expand Up @@ -444,7 +447,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
[Op.gte]: new Date(),
},
visibility: EntityVisibility.Visible,
allowedRoles: { [Op.contains]: [req.user.role] },
allowedRoles: { [Op.overlap]: req.user.roles },
offices: { [Op.contains]: [req.office.id] },
},
order: ['startDate'],
Expand Down Expand Up @@ -574,7 +577,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
if (event.visibility === EntityVisibility.None) {
return reply.throw.accessDenied()
}
if (!event.allowedRoles.includes(req.user.role)) {
if (!fp.hasIntersection(event.allowedRoles, req.user.roles)) {
return reply.throw.accessDenied()
}
}
Expand Down Expand Up @@ -761,7 +764,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
},
'metadata.global': true,
visibility: EntityVisibility.Visible,
allowedRoles: { [Op.contains]: [req.user.role] },
allowedRoles: { [Op.overlap]: req.user.roles },
},
],
},
Expand Down Expand Up @@ -789,7 +792,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
},
}
if (!req.can(Permissions.AdminManage)) {
where.allowedRoles = { [Op.contains]: [req.user.role] }
where.allowedRoles = { [Op.overlap]: req.user.roles }
}
const event = await fastify.db.Event.findOne({ where })
if (!event) {
Expand Down Expand Up @@ -1162,7 +1165,7 @@ const adminRouter: FastifyPluginCallback = async function (fastify, opts) {
Permissions.AdminReceiveNotifications
)
return fastify.db.User.findAllActive({
where: { role: { [Op.in]: roles } },
where: { roles: { [Op.overlap]: roles } },
})
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/forms/client/components/AdminForms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const AdminForms = () => {
Header: 'Creator',
accessor: (form: FormAdminResponse) => {
const user = usersById[form.creatorUserId]
return <UserLabel user={user} hideRole />
return <UserLabel user={user} />
},
},
{
Expand Down
7 changes: 4 additions & 3 deletions src/modules/forms/server/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Op, Filterable } from 'sequelize'
import { appConfig } from '#server/app-config'
import { getJSONdiff } from '#server/utils'
import { EntityVisibility } from '#shared/types'
import * as fp from '#shared/utils/fp'
import { User } from '#modules/users/types'
import { useRateLimit } from '#server/utils/rate-limit'
import { Permissions } from '../permissions'
Expand Down Expand Up @@ -47,7 +48,7 @@ const publicRouter: FastifyPluginCallback = async (fastify, opts) => {
return reply.throw.notFound()
}
if (form.visibility !== EntityVisibility.UrlPublic) {
if (!form.allowedRoles.includes(req.user.role)) {
if (!fp.hasIntersection(form.allowedRoles, req.user.roles)) {
return reply.throw.notFound()
}
}
Expand Down Expand Up @@ -97,7 +98,7 @@ const publicRouter: FastifyPluginCallback = async (fastify, opts) => {
return reply.throw.gone()
}
if (form.visibility !== EntityVisibility.UrlPublic) {
if (!form.allowedRoles.includes(req.user.role)) {
if (!fp.hasIntersection(form.allowedRoles, req.user.roles)) {
return reply.throw.notFound()
}
}
Expand Down Expand Up @@ -383,7 +384,7 @@ const adminRouter: FastifyPluginCallback = async (fastify, opts) => {
Permissions.AdminReceiveNotifications
)
return fastify.db.User.findAllActive({
where: { role: { [Op.in]: roles } },
where: { roles: { [Op.overlap]: roles } },
})
})

Expand Down
2 changes: 1 addition & 1 deletion src/modules/guest-invites/server/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ const adminRouter: FastifyPluginCallback = async function (fastify, opts) {
{
email: invite.email,
fullName: invite.fullName,
role: appConfig.getDefaultUserRoleByEmail(invite.email),
roles: [appConfig.getDefaultUserRoleByEmail(invite.email)],
},
{ transaction: t }
)
Expand Down
2 changes: 1 addition & 1 deletion src/modules/news/client/components/AdminNews.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const _AdminNews = () => {
Header: 'Creator',
accessor: (one: any) => {
const user = usersById[one.creatorUserId]
return <UserLabel user={user} hideRole />
return <UserLabel user={user} />
},
},
{
Expand Down
4 changes: 2 additions & 2 deletions src/modules/news/server/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
order: [['publishedAt', 'DESC']],
where: {
visibility: EntityVisibility.Visible,
allowedRoles: { [Op.contains]: [req.user.role] },
allowedRoles: { [Op.overlap]: req.user.roles },
offices: { [Op.contains]: [req.office.id] },
published: true,
},
Expand All @@ -39,7 +39,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) {
where.visibility = {
[Op.in]: [EntityVisibility.Visible, EntityVisibility.Url],
}
where.allowedRoles = { [Op.contains]: [req.user.role] }
where.allowedRoles = { [Op.overlap]: req.user.roles }
where.published = true
}
const newsArticle = await fastify.db.News.findOne({ where })
Expand Down
12 changes: 6 additions & 6 deletions src/modules/office-visits/client/components/VisitsStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const _VisitsStats: React.FC = () => {
{ enabled: isShown && period.from.isValid() && period.to.isValid() }
)

const onToggleDepartmentStats = React.useCallback(
const onToggleRolesStats = React.useCallback(
(date: string) => (ev: React.MouseEvent) => {
setExpandedDates((dates) => toggleInArray(dates, date))
},
Expand Down Expand Up @@ -77,18 +77,18 @@ const _VisitsStats: React.FC = () => {
className="ml-2"
size="small"
kind="secondary"
onClick={onToggleDepartmentStats(x.date)}
onClick={onToggleRolesStats(x.date)}
>
{isExpanded ? 'Less' : 'More'} info
</Button>
)}
</div>
{isExpanded && (
<div className="mt-2">
{x.occupancyPercentByDepartment.map((d) => (
<div key={d.department}>
{d.department}{' '}
{Number((d.occupancyPercent * 100).toFixed(1))}%
{x.occupancyPercentByRole.map((d) => (
<div key={d.role}>
{d.role} {Number((d.occupancyPercent * 100).toFixed(1))}
%
</div>
))}
{!!x.guests.length && (
Expand Down
11 changes: 11 additions & 0 deletions src/modules/office-visits/metadata-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { z } from 'zod'

export const schema = z
.object({
statistics: z.object({
splitByRoleGroup: z.string(),
}),
})
.strict()

export type Metadata = z.infer<typeof schema>
Loading

0 comments on commit 855d1d3

Please sign in to comment.