Skip to content

Commit

Permalink
Konstantin/rka 21 add job actions (#5)
Browse files Browse the repository at this point in the history
* rka-21: move files around

* rka-21: add job card actions

* rka-21: clean up layout a bit

* rka-21: add husky pre-commits and fix types
  • Loading branch information
konstrybakov authored Jun 12, 2024
1 parent 63c7723 commit e648370
Show file tree
Hide file tree
Showing 28 changed files with 491 additions and 35 deletions.
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bun typecheck
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { Box, Button, Flex, Text, TextField } from '@radix-ui/themes'
import { useAtomValue } from 'jotai'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { platformAtom, trackerURLAtom } from '../state'

import { platformAtom, trackerURLAtom } from '../../state'
import { actionCreateCompany } from './actions/create-company'

const schema = z.object({
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { Flex, RadioCards, Text } from '@radix-ui/themes'

import { useAtom } from 'jotai'
import { variantAtom } from '../state'
import { variantAtom } from '../../state'
import type { Variant } from './types'

export const VariantPicker = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import { useAtomValue } from 'jotai'
import { variantAtom } from '../state'
import { variantAtom } from '../../state'
import { VariantURL } from './variants/url'

// TODO: Rethink the renderer
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useAtom, useSetAtom } from 'jotai'
import Image from 'next/image'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { platformAtom, trackerURLAtom } from '../../state'
import { platformAtom, trackerURLAtom } from '../../../state'
import { actionCheckURL } from '../actions/check-url'

const schema = z.object({
Expand Down
6 changes: 3 additions & 3 deletions app/add/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { isAdmin } from '@/lib/auth/is-admin'
import { type User, currentUser } from '@clerk/nextjs/server'
import { Provider } from 'jotai'
import { redirect } from 'next/navigation'
import { CompanyName } from './company/company-data'
import { VariantPicker } from './variant/picker'
import { VariantRenderer } from './variant/renderer'
import { CompanyName } from './components/company/company-data'
import { VariantPicker } from './components/variant/picker'
import { VariantRenderer } from './components/variant/renderer'

export default async function Add() {
const user = (await currentUser()) as User
Expand Down
2 changes: 1 addition & 1 deletion app/add/state.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { HiringPlatformName } from '@/lib/db/schema'
import { atom } from 'jotai'
import type { Variant } from './variant/types'
import type { Variant } from './components/variant/types'

export const variantAtom = atom<Variant>('url')
export const platformAtom = atom<HiringPlatformName | null>(null)
Expand Down
32 changes: 32 additions & 0 deletions app/components/list/action-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Button } from '@radix-ui/themes'
import type { BaseButtonProps } from '@radix-ui/themes/dist/esm/components/base-button.js'
import type { ReactNode } from 'react'

type ActionButtonProps = {
loading: boolean
clickHandler: () => void
isActive: boolean
colorActive: BaseButtonProps['color']
icon: ReactNode
label: string
}

export const ActionButton = ({
loading,
clickHandler,
isActive,
colorActive,
icon,
label,
}: ActionButtonProps) => (
<Button
loading={loading}
onClick={clickHandler}
size="1"
color={isActive ? colorActive : 'gray'}
variant="soft"
>
{icon}
{label}
</Button>
)
31 changes: 31 additions & 0 deletions app/components/list/actions/create-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { SelectJob } from '@/lib/db/schema'
import { logger } from '@/lib/logger'
import type { ActionResponse } from '@/lib/types/api'

export const createAction =
<QR>(query: (jobId: SelectJob['id'], property: boolean) => Promise<QR>) =>
async (
jobId: SelectJob['id'],
property: boolean,
): Promise<ActionResponse<QR>> => {
const l = logger.child({ jobId, property })

try {
const result = await query(jobId, property)

return {
data: result,
error: false,
}
} catch (error) {
l.error(error)

return {
error: true,
errorMessage:
error instanceof Error
? error.message
: '[createAction] Unknown error',
}
}
}
11 changes: 11 additions & 0 deletions app/components/list/actions/mark-property.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use server'
import {
queryMarkHidden,
queryMarkSeen,
queryMarkTopChoice,
} from '@/lib/db/queries/job-action'
import { createAction } from './create-action'

export const actionMarkTopChoice = createAction(queryMarkTopChoice)
export const actionMarkHidden = createAction(queryMarkHidden)
export const actionMarkSeen = createAction(queryMarkSeen)
34 changes: 34 additions & 0 deletions app/components/list/hooks/use-job-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { SelectJob } from '@/lib/db/schema'
import { useState } from 'react'
import type { createAction } from '../actions/create-action'

export const useJobAction = (
initialState: boolean,
action: ReturnType<typeof createAction>,
) => {
const [isActive, setIsActive] = useState(initialState)
const [loading, setLoading] = useState(false)

const clickHandler = async (jobId: SelectJob['id']) => {
const newState = !isActive

setLoading(true)

try {
const result = await action(jobId, newState)

if (result.error) {
// TODO: Add toast notifications
console.error(result.errorMessage)
} else {
setIsActive(newState)
}
} catch (error) {
console.error(error)
} finally {
setLoading(false)
}
}

return { isActive, loading, clickHandler }
}
53 changes: 53 additions & 0 deletions app/components/list/job-card-actions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use client'
import type { SelectJob } from '@/lib/db/schema'
import {
EyeNoneIcon,
EyeOpenIcon,
LightningBoltIcon,
} from '@radix-ui/react-icons'
import { Text } from '@radix-ui/themes'
import { ActionButton } from './action-button'
import {
actionMarkHidden,
actionMarkSeen,
actionMarkTopChoice,
} from './actions/mark-property'
import { useJobAction } from './hooks/use-job-action'

export const JobCardActions = ({ job }: { job: SelectJob }) => {
const hidden = useJobAction(job.isHidden, actionMarkHidden)
const topChoice = useJobAction(job.isTopChoice, actionMarkTopChoice)
const seen = useJobAction(job.isSeen, actionMarkSeen)

return (
<>
<Text size="2" color="gray">
Mark as:
</Text>
<ActionButton
isActive={hidden.isActive}
loading={hidden.loading}
clickHandler={() => hidden.clickHandler(job.id)}
colorActive="sky"
icon={<EyeNoneIcon />}
label="Hidden"
/>
<ActionButton
isActive={topChoice.isActive}
loading={topChoice.loading}
clickHandler={() => topChoice.clickHandler(job.id)}
colorActive="plum"
icon={<LightningBoltIcon />}
label="Top Choice"
/>
<ActionButton
isActive={seen.isActive}
loading={seen.loading}
clickHandler={() => seen.clickHandler(job.id)}
colorActive="grass"
icon={<EyeOpenIcon />}
label="Seen"
/>
</>
)
}
8 changes: 6 additions & 2 deletions app/job-card.tsx → app/components/list/job-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Text,
} from '@radix-ui/themes'
import NextLink from 'next/link'
import { JobCardActions } from './job-card-actions'

type JobCardProps = {
job: Awaited<QueryGetJobsResult>[number]
Expand All @@ -21,8 +22,8 @@ type JobCardProps = {
export const JobCard = async ({ job }: JobCardProps) => {
return (
<Card>
<Grid columns="2" gap="1">
<Flex gap="1" align="center">
<Grid columns="2" gap="2">
<Flex gap="2" align="center">
<Heading size="3">{job.title} </Heading>
<Link trim="end" asChild>
<NextLink target="_blank" href={job.url}>
Expand Down Expand Up @@ -53,6 +54,9 @@ export const JobCard = async ({ job }: JobCardProps) => {
}).format(new Date(job.lastUpdatedAt))}
</Text>
</Flex>
<Flex align="center" gridColumn="1/-1" gap="2" justify="end">
<JobCardActions job={job} />
</Flex>
</Grid>
</Card>
)
Expand Down
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Box, Container, Flex, Section, Theme } from '@radix-ui/themes'
import type { Metadata } from 'next'

import '@radix-ui/themes/styles.css'
import { Navigation } from './navigation'
import { Navigation } from './components/nav/navigation'

export const metadata: Metadata = {
title: 'OWAT!',
Expand All @@ -28,7 +28,7 @@ export default function RootLayout({
<Theme accentColor="amber" grayColor="sand" radius="small">
<Section size="1">
<Container>
<Flex align="center" justify="between">
<Flex height="28px" align="center" justify="between">
<Navigation />
<Flex align="center" asChild>
<Box>
Expand Down
35 changes: 16 additions & 19 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { isAdmin } from '@/lib/auth/is-admin'
import { type User, currentUser } from '@clerk/nextjs/server'
import { PlusIcon } from '@radix-ui/react-icons'
import { Box, Button, Heading, Section } from '@radix-ui/themes'
import { Box, Button, Flex, Heading, Section } from '@radix-ui/themes'
import Link from 'next/link'
import { JobList } from './job-list'
import { JobList } from './components/list/job-list'

export default async function Home() {
// TODO: Fix user management
Expand All @@ -13,25 +13,22 @@ export default async function Home() {
return (
<>
<Section size="1">
<Box mb="4">
<Flex align="center" justify="between">
<Heading>Oh! What a tracker!</Heading>
</Box>
</Section>
{isAdmin(user) && (
<Section size="1">
<Box>
<Link href="/add">
<Button>
<PlusIcon />
Add a company
</Button>
</Link>
</Box>
</Section>
)}
<Section size="1">
<JobList />
{isAdmin(user) && (
<Box>
<Link href="/add">
<Button>
<PlusIcon />
Add a company
</Button>
</Link>
</Box>
)}
</Flex>
</Section>

<JobList />
</>
)
}
Binary file modified bun.lockb
Binary file not shown.
4 changes: 4 additions & 0 deletions drizzle/0007_ancient_lady_bullseye.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE "jobs" ADD COLUMN "is_seen" boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE "jobs" ADD COLUMN "is_hidden" boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE "jobs" ADD COLUMN "is_top_choice" boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE "jobs" ADD COLUMN "is_applied" boolean DEFAULT false NOT NULL;
Loading

0 comments on commit e648370

Please sign in to comment.