Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "daily-helper",
"version": "2.2.0",
"version": "2.3.0",
"private": true,
"scripts": {
"start": "vite",
Expand All @@ -20,6 +20,7 @@
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"motion": "^12.38.0",
"react-markdown": "^9.0.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"typescript": "^5.9.3"
Expand Down
769 changes: 689 additions & 80 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
overrides:
postcss: "8.5.14"
141 changes: 141 additions & 0 deletions src/components/PrNoteButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { useState, useRef } from 'react'
import NoteAltIcon from '@mui/icons-material/NoteAlt'
import NoteAltOutlinedIcon from '@mui/icons-material/NoteAltOutlined'
import Box from '@mui/material/Box'
import IconButton from '@mui/material/IconButton'
import Popover from '@mui/material/Popover'
import Stack from '@mui/material/Stack'
import Tooltip from '@mui/material/Tooltip'
import Typography from '@mui/material/Typography'
import NoteEditor from '../views/Notes/NoteEditor'
import { NoteCard, TodoNoteCard } from '../views/Notes/NoteGroup'
import { notesHandler } from '../helpers/notesHandler'

type PrNoteButtonProps = {
prId: string
prNumber: number
prTitle: string
prUrl: string
repositoryName: string
size?: 'small' | 'medium'
}

export default function PrNoteButton({
prId,
prNumber,
prTitle,
prUrl,
repositoryName,
size = 'medium',
}: PrNoteButtonProps) {
const [note, setNote] = useState<NoteItem | null>(() =>
notesHandler.getPrNote(repositoryName, prId, prNumber, prTitle),
)
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)
const buttonRef = useRef<HTMLButtonElement>(null)

const hasNote =
note !== null &&
(note.content?.trim() !== '' || (note.items?.length ?? 0) > 0)

const groupId = note ? (notesHandler.findNoteGroupId(note.id) ?? '') : ''

const refreshNote = () =>
setNote(notesHandler.getPrNote(repositoryName, prId, prNumber, prTitle))

// Creating a brand-new markdown note via the popover
const handleCreate = (content: string) => {
notesHandler.savePrNote({
prId,
prNumber,
prTitle,
prUrl,
repositoryName,
content,
})
refreshNote()
}

return (
<>
<Tooltip title={hasNote ? 'View note' : 'Add note'}>
<IconButton
ref={buttonRef}
size={size}
onClick={() => setAnchorEl(buttonRef.current)}
sx={{
color: hasNote ? 'primary.main' : 'text.disabled',
p: size === 'small' ? 0.25 : 0.5,
}}
>
{hasNote ? (
<NoteAltIcon fontSize={size === 'small' ? 'small' : 'medium'} />
) : (
<NoteAltOutlinedIcon
fontSize={size === 'small' ? 'small' : 'medium'}
/>
)}
</IconButton>
</Tooltip>

<Popover
open={Boolean(anchorEl)}
anchorEl={anchorEl}
onClose={() => setAnchorEl(null)}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
disableRestoreFocus
>
<Box sx={{ minWidth: 400, maxWidth: 600, p: 2 }}>
<Stack spacing={1.5}>
<Stack direction="row" alignItems="baseline" spacing={0.5}>
<Typography variant="caption" color="text.secondary">
Note for
</Typography>
<Typography
variant="caption"
fontWeight={600}
sx={{ fontFamily: 'monospace' }}
>
{repositoryName}
</Typography>
<Typography variant="caption" color="text.secondary">
#{prNumber}
</Typography>
</Stack>

{note === null ? (
<NoteEditor value="" onSave={handleCreate} minRows={5} />
) : note.type === 'todo' ? (
<TodoNoteCard
note={note}
groupId={groupId}
onDelete={() => {
notesHandler.deleteNote(groupId, note.id)
setNote(null)
setAnchorEl(null)
}}
onTodoChange={refreshNote}
/>
) : (
<NoteCard
note={note}
groupId={groupId}
onUpdate={content => {
notesHandler.updateNote(groupId, note.id, content)
refreshNote()
}}
onDelete={() => {
notesHandler.deleteNote(groupId, note.id)
setNote(null)
setAnchorEl(null)
}}
onReload={refreshNote}
/>
)}
</Stack>
</Box>
</Popover>
</>
)
}
26 changes: 22 additions & 4 deletions src/components/PullRequestFilterBar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import GroupIcon from '@mui/icons-material/Group'
import NoteAltOutlinedIcon from '@mui/icons-material/NoteAltOutlined'
import PersonIcon from '@mui/icons-material/Person'
import Stack from '@mui/material/Stack'
import StarRate from '@mui/icons-material/StarRate'
Expand Down Expand Up @@ -49,8 +50,8 @@ function FilterButton({
}

type ViewToggleProps = {
activeView: 'list' | 'kanban'
onViewToggle: (view: 'list' | 'kanban') => void
activeView: 'list' | 'kanban' | 'notes'
onViewToggle: (view: 'list' | 'kanban' | 'notes') => void
}
function ViewToggle({ activeView, onViewToggle }: ViewToggleProps) {
return (
Expand Down Expand Up @@ -99,6 +100,23 @@ function ViewToggle({ activeView, onViewToggle }: ViewToggleProps) {
<ViewKanbanOutlinedIcon sx={{ fontSize: 18 }} />
</Stack>
</Tooltip>
<Tooltip title="Notes view">
<Stack
alignItems="center"
justifyContent="center"
onClick={() => onViewToggle('notes')}
sx={{
px: 0.75,
py: 0.5,
cursor: 'pointer',
color: activeView === 'notes' ? 'primary.main' : 'text.secondary',
bgcolor: activeView === 'notes' ? 'action.selected' : 'transparent',
'&:hover': { bgcolor: 'action.hover' },
}}
>
<NoteAltOutlinedIcon sx={{ fontSize: 18 }} />
</Stack>
</Tooltip>
</Stack>
)
}
Expand All @@ -110,8 +128,8 @@ type ReviewFilterBarProps = {
onMyPrsToggle: VoidFunction
isMyWorkActive: boolean
onMyWorkToggle: VoidFunction
activeView: 'list' | 'kanban'
onViewToggle: (view: 'list' | 'kanban') => void
activeView: 'list' | 'kanban' | 'notes'
onViewToggle: (view: 'list' | 'kanban' | 'notes') => void
}
export default function PullRequestFilterBar({
isMustReviewActive,
Expand Down
Loading
Loading