Skip to content

Commit

Permalink
Merge branch 'main' into fix/security-updates
Browse files Browse the repository at this point in the history
  • Loading branch information
nlsvgtr committed May 23, 2024
2 parents ce36f45 + a9964dc commit 0ebcce9
Show file tree
Hide file tree
Showing 156 changed files with 9,192 additions and 30,168 deletions.
1 change: 1 addition & 0 deletions apps/admin-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@openstad-headless/likes": "file:../../packages/likes",
"@openstad-headless/raw-resource": "file:../../packages/raw-resource",
"@openstad-headless/resource-detail": "file:../../packages/resource-detail",
"@openstad-headless/resource-detail-with-map": "file:../../packages/resource-detail-with-map",
"@openstad-headless/resource-form": "file:../../packages/resource-form",
"@openstad-headless/resource-overview": "file:../../packages/resource-overview",
"@openstad-headless/resource-overview-with-map": "file:../../packages/resource-overview-with-map",
Expand Down
2 changes: 1 addition & 1 deletion apps/admin-server/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async function authMiddleware(req: NextRequest, res: NextResponse) {
if (!response.ok) throw new Error('TokenValidationFailed')
let result:OpenstadProfile = await response.json();
if (!result.id) throw 'no user'
if ( !( req.nextUrl.pathname.match(/^\/(?:projects)?\/?/) && hasRole(result, 'member') ) // project overview is available for members; anything else requires
if ( !( req.nextUrl.pathname.match(/^\/(?:projects)?\/?$/) && hasRole(result, 'member') ) // project overview is available for members; anything else requires
&& result.role != 'superuser'
&& result.role != 'admin' ) {
forceNewLogin = true;
Expand Down
2 changes: 1 addition & 1 deletion apps/admin-server/src/components/ui/sidenav-project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export function SidenavProject({ className }: { className?: string }) {
variant={location.includes('/comments') ? 'secondary' : 'ghost'}
className="w-full flex justify-start"
onClick={(e) => {}}>
<span className="truncate">Argumenten</span>
<span className="truncate">Reacties</span>
</Button>
</Link>
<Link href={`/projects/${project}/submissions`}>
Expand Down
23 changes: 14 additions & 9 deletions apps/admin-server/src/components/ui/sortTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,23 @@ const sortFunctions = {
'voted-yes': (a: any, b: any) => b.resource?.yes || 0 - a.resource?.yes || 0,
'voted-no': (a: any, b: any) => b.resource?.no || 0 - a.resource?.no || 0,
'name': (a: any, b: any) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()),
'url': (a: any, b: any) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()),
'email': (a: any, b: any) => { let aEmail = a?.email || ''; let bEmail = b?.email || ''; return aEmail.toLowerCase().localeCompare(bEmail.toLowerCase()) },
'postcode': (a: any, b: any) => { let aPostcode = a?.postcode || ''; let bPostcode = b?.postcode || ''; return aPostcode.toLowerCase().localeCompare(bPostcode.toLowerCase()) },
'code': (a: any, b: any) => b.code - a.code,
'ip': (a: any, b: any) => b.ip - a.ip,
'userId': (a: any, b: any) => b.userId - a.userId,

'endDate': (a: any, b: any) => new Date(b.config?.project?.endDate).getTime() - new Date(a.config?.project?.endDate).getTime(),
'votesIsActive': (a: any, b: any) => ( b.config.votes.isActive ? 1 : -1 ) - ( a.config.votes.isActive ? 1 : -1 ),
'commentsIsActive': (a: any, b: any) => ( b.config.comments.canComment ? 1 : -1 ) - ( a.config.comments.canComment ? 1 : -1 ),
};

export const sortTable = (sortType: string, el: React.MouseEvent<HTMLElement, MouseEvent>, data: Array<any>) => {

const sortFunction = sortFunctions[sortType as keyof typeof sortFunctions];
if (!sortFunction) {
return;
return data;
}

const filterButtons = document.querySelectorAll('.filter-button');
filterButtons.forEach(button => button.classList.remove('font-bold'));
filterButtons.forEach(button => button.classList.remove('text-black'));
Expand All @@ -39,17 +44,17 @@ export const sortTable = (sortType: string, el: React.MouseEvent<HTMLElement, Mo
return sortedWidgets;
};

export const searchTable = (setData: Function, delay: number = 500) => {
export const searchTable = (setData: Function, type?: string, delay: number = 250) => {
let timerId: NodeJS.Timeout;

const debouncedSearchTable = (searchTerm: string, data: Array<any> = [], originalData: Array<any> = []) => {
clearTimeout(timerId);
timerId = setTimeout(() => {
if (searchTerm.length >= 3) {
if (searchTerm.length >= 1) {
const searchResult = data.filter(item =>
Object.values(item).some(val =>
String(val).toLowerCase().includes(searchTerm.toLowerCase())
)
type ? String(eval(`item.${type}`)).toLowerCase().includes(searchTerm.toLowerCase())
: Object.values(item).some(val =>
String(val).toLowerCase().includes(searchTerm.toLowerCase())
)
);
setData(searchResult);
} else {
Expand Down
16 changes: 14 additions & 2 deletions apps/admin-server/src/hooks/use-project-list.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import useSWR from 'swr';

export default function useProjectList() {
const projectListSwr = useSWR(`/api/openstad/api/project?includeConfig=1`);
type paramsType = {
projectsWithIssues?: boolean,
}


export default function useProjectList(params?: paramsType) {

let projectListSwrKey =`/api/openstad/api/project?includeConfig=1`;

if (params?.projectsWithIssues) {
projectListSwrKey = `/api/openstad/api/project/issues`;
}

let projectListSwr = useSWR(projectListSwrKey);
return { ...projectListSwr };

}
5 changes: 3 additions & 2 deletions apps/admin-server/src/lib/widget-definitions.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const WidgetDefinitions = {
agenda: 'Agenda',
comments: 'Argumenten',
comments: 'Reacties',
begrootmodule: 'Begrootmodule',
enquete: 'Enquete',
resourcesmap: 'Resource map',
Expand All @@ -13,7 +13,8 @@ export const WidgetDefinitions = {
resourcedetail: 'Resource detail',
resourceform: 'Resource form',
resourceoverview: 'Resource overview',
resourcewithmap: 'Resource with map'
resourcewithmap: 'Resource with map',
resourcedetailwithmap: 'Resource detail with map'
};

export type WidgetDefinition = keyof typeof WidgetDefinitions;
17 changes: 9 additions & 8 deletions apps/admin-server/src/pages/issues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useRouter } from 'next/router';
import projectListSwr from '../hooks/use-project-list';

export default function Projects() {
const { data, isLoading, error } = projectListSwr();
const { data } = projectListSwr({projectsWithIssues: true});
const router = useRouter();

if (!data) return null;
Expand Down Expand Up @@ -37,7 +37,7 @@ export default function Projects() {
]}>
<div className="container py-6">
<div className="p-6 bg-white rounded-md">
<div className="grid grid-cols-1 lg:grid-cols-11 items-center py-2 px-2 border-b border-border">
<div className="grid grid-cols-1 lg:grid-cols-4 items-center py-2 px-2 border-b border-border">
{headers.map((header) => (
<ListHeading className="hidden lg:flex" key={header}>
{header}
Expand All @@ -51,14 +51,14 @@ export default function Projects() {
if (currentDate > project.config.project.endDate && project.config.project.endDate != null) {
return (
<li
className="grid grid-cols-2 lg:grid-cols-11 items-center py-3 px-2 h-16 hover:bg-secondary-background hover:cursor-pointer border-b border-border gap-2"
className="grid grid-cols-2 lg:grid-cols-4 items-center py-3 px-2 h-16 hover:bg-secondary-background hover:cursor-pointer border-b border-border gap-2"
key={project.id}
onClick={(d) => {
router.push(`/projects/${project.id}/widgets`);
}}>
<Paragraph className="truncate">{project.name}</Paragraph>
<Paragraph className="hidden lg:flex truncate">
{project.createdAt}
{new Date(project.createdAt).toLocaleDateString("nl-NL")}
</Paragraph>
<Paragraph className="hidden lg:flex truncate -mr-16">
Einddatum is geweest, maar het project loopt nog.
Expand All @@ -75,17 +75,18 @@ export default function Projects() {
if (currentDate > anonymizationDate && project.config.project.endDate != null) {
return (
<li
className="grid grid-cols-2 lg:grid-cols-11 items-center py-3 px-2 h-16 hover:bg-secondary-background hover:cursor-pointer border-b border-border gap-2"
className="grid grid-cols-2 lg:grid-cols-4 items-center py-3 px-2 h-16 hover:bg-secondary-background hover:cursor-pointer border-b border-border gap-2"
key={project.id}
onClick={(d) => {
router.push(`/projects/${project.id}/widgets`);
}}>
<Paragraph className="truncate">{project.name}</Paragraph>
<Paragraph className="hidden lg:flex truncate">
{project.createdAt}
{new Date(project.createdAt).toLocaleDateString("nl-NL")}
</Paragraph>
<Paragraph className="hidden lg:flex truncate -mr-16">
De gebruikers van het project moeten geanonimiseerd worden.
<Paragraph className="hidden lg:flex -mr-16">
{ project.issue == 'Project has ended but is not yet anonymized' && 'De gebruikers van het project moeten geanonimiseerd worden.' }
{ project.issue == 'Project endDate is in the past but projectHasEnded is not set' && 'De einddatum van het project is in het verleden maar het project is nog niet beëindigd' }
</Paragraph>
<Paragraph className="flex">
<ChevronRight
Expand Down
28 changes: 20 additions & 8 deletions apps/admin-server/src/pages/projects/[project]/areas/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export default function ProjectAreas() {
const { data, removeArea } = useArea(project as string);

const [filterData, setFilterData] = useState(data);
const debouncedSearchTable = searchTable(setFilterData);
const [filterSearchType, setFilterSearchType] = useState<string>('');
const debouncedSearchTable = searchTable(setFilterData, filterSearchType);

useEffect(() => {
setFilterData(data);
Expand Down Expand Up @@ -46,12 +47,23 @@ export default function ProjectAreas() {
}>
<div className="container py-6">

<input
type="text"
className='mb-4 p-2 rounded float-right'
placeholder="Zoeken..."
onChange={(e) => debouncedSearchTable(e.target.value, filterData, data)}
/>
<div className="float-right mb-4 flex gap-4">
<p className="text-xs font-medium text-muted-foreground self-center">Filter op:</p>
<select
className="p-2 rounded"
onChange={(e) => setFilterSearchType(e.target.value)}
>
<option value="">Alles</option>
<option value="id">Stem ID</option>
<option value="name">Naam</option>
</select>
<input
type="text"
className='p-2 rounded'
placeholder="Zoeken..."
onChange={(e) => debouncedSearchTable(e.target.value, filterData, data)}
/>
</div>

<div className="p-6 bg-white rounded-md clear-right">
<div className="grid grid-cols-1 lg:grid-cols-4 items-center py-2 px-2 border-b border-border">
Expand All @@ -61,7 +73,7 @@ export default function ProjectAreas() {
</button>
</ListHeading>
<ListHeading className="hidden lg:flex">
<button className="filter-button" onClick={(e) => setFilterData(sortTable('name', e, filterData))}>
<button className="filter-button" onClick={(e) => setFilterData(sortTable('name', e, filterData))}>
Naam
</button>
</ListHeading>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,18 @@ export default function ProjectCommentEdit() {
url: '/projects',
},
{
name: 'Argumenten',
name: 'Reacties',
url: `/projects/${project}/comments`,
},
{
name: 'Argument aanpassen',
name: 'Reactie aanpassen',
url: `/projects/${project}/comments/${id}`,
},
]}>
<div className="container py-6">
<div className="p-6 bg-white rounded-md">
<Form {...form}>
<Heading size="xl">Argument aanpassen</Heading>
<Heading size="xl">Reactie aanpassen</Heading>
<Separator className="my-4" />
<form
onSubmit={form.handleSubmit(onSubmit)}
Expand Down
78 changes: 56 additions & 22 deletions apps/admin-server/src/pages/projects/[project]/comments/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ export default function ProjectComments() {
if (data) {
let comments = []
for (let i = 0; i < data.length; i++) {
if(data[i]?.commentsFor) {
if (data[i]?.commentsFor) {
for (let j = 0; j < data[i]?.commentsFor.length; j++) {
comments.push(data[i]?.commentsFor[j])
}
}
if(data[i]?.commentsAgainst) {
if (data[i]?.commentsAgainst) {
for (let k = 0; k < data[i]?.commentsAgainst.length; k++) {
comments.push(data[i]?.commentsAgainst[k])
}
Expand All @@ -35,54 +35,88 @@ export default function ProjectComments() {
}
}, [data]);

const [filterData, setFilterData] = useState(data);
const debouncedSearchTable = searchTable(setFilterData);
const [filterData, setFilterData] = useState(comments);
const [filterSearchType, setFilterSearchType] = useState<string>('');
const debouncedSearchTable = searchTable(setFilterData, filterSearchType);

useEffect(() => {
setFilterData(data);
}, [data])
setFilterData(comments);
}, [comments])

return (
<div>
<PageLayout
pageHeader="Projecten"
breadcrumbs={[
{
{
name: 'Projecten',
url: '/projects',
},
{
name: 'Argumenten',
name: 'Reacties',
url: `/projects/${project}/comments`,
},
]}>
<div className="container py-6">
<div className="p-6 bg-white rounded-md">

<div className="float-right mb-4 flex gap-4">
<p className="text-xs font-medium text-muted-foreground self-center">Filter op:</p>
<select
className="p-2 rounded"
onChange={(e) => setFilterSearchType(e.target.value)}
>
<option value="">Alles</option>
<option value="id">Reactie ID</option>
<option value="resourceId">Resource ID</option>
<option value="createdAt">Geplaatst op</option>
<option value="sentiment">Sentiment</option>
</select>
<input
type="text"
className='p-2 rounded'
placeholder="Zoeken..."
onChange={(e) => debouncedSearchTable(e.target.value, filterData, comments)}
/>
</div>

<div className="p-6 bg-white rounded-md clear-right">
<div className="grid grid-cols-1 lg:grid-cols-7 items-center py-2 px-2 border-b border-border">
<ListHeading className="hidden lg:flex lg:col-span-2">
<button className="filter-button" onClick={(e) => setFilterData(sortTable('id', e, filterData))}>
Argument ID
<button className="filter-button" onClick={(e) => {
const sortedData = sortTable('id', e, filterData);
setFilterData(sortedData ? sortedData : []);
}}>
Reactie ID
</button>
</ListHeading>
<ListHeading className="hidden lg:flex lg:col-span-1">
<button className="filter-button" onClick={(e) => setFilterData(sortTable('resourceId', e, filterData))}>
<button className="filter-button" onClick={(e) => {
const sortedData = sortTable('id', e, filterData);
setFilterData(sortedData ? sortedData : []);
}}>
Resource ID
</button>
</ListHeading>
<ListHeading className="hidden lg:flex lg:col-span-2">
<button className="filter-button" onClick={(e) => setFilterData(sortTable('createdAt', e, filterData))}>
<button className="filter-button" onClick={(e) => {
const sortedData = sortTable('id', e, filterData);
setFilterData(sortedData ? sortedData : []);
}}>
Geplaatst op
</button>
</ListHeading>
<ListHeading className="hidden lg:flex lg:col-span-1">
<button className="filter-button" onClick={(e) => setFilterData(sortTable('sentiment', e, filterData))}>
<button className="filter-button" onClick={(e) => {
const sortedData = sortTable('id', e, filterData);
setFilterData(sortedData ? sortedData : []);
}}>
Sentiment
</button>
</ListHeading>
</div>
<ul>
{comments?.map((comment: any) => (
<Link href={`/projects/${project}/comments/${comment.id}`} key={comment.id}>
{filterData?.map((comment: any) => (
<Link href={`/projects/${project}/comments/${comment.id}`} key={comment.id}>
<li key={comment.id} className="grid grid-cols-3 lg:grid-cols-7 items-center py-3 px-2 hover:bg-muted hover:cursor-pointer transition-all duration-200 border-b">
<div className="col-span-2 truncate">
<Paragraph>{comment.id}</Paragraph>
Expand All @@ -93,7 +127,7 @@ export default function ProjectComments() {
e.preventDefault();
router.push(`/projects/${project}/resources/${comment.resourceId}`);
}}
style={{textDecoration: 'underline', zIndex: '1'}}>{comment.resourceId}
style={{ textDecoration: 'underline', zIndex: '1' }}>{comment.resourceId}
</a>
</Paragraph>
<Paragraph className="hidden lg:flex truncate lg:col-span-2">
Expand All @@ -106,21 +140,21 @@ export default function ProjectComments() {
className="hidden lg:flex ml-auto"
onClick={(e) => e.preventDefault()}>
<RemoveResourceDialog
header="Argument verwijderen"
message="Weet je zeker dat je deze argument wilt verwijderen?"
header="Reactie verwijderen"
message="Weet je zeker dat je deze reactie wilt verwijderen?"
onDeleteAccepted={() =>
removeComment(comment.id)
.then(() =>
toast.success('Argument successvol verwijderd')
toast.success('Reactie successvol verwijderd')
)
.catch((e) =>
toast.error('Argument kon niet worden verwijderd')
toast.error('Reactie kon niet worden verwijderd')
)
}
/>
</div>
</li>
</Link>
</Link>
))}
</ul>
</div>
Expand Down
Loading

0 comments on commit 0ebcce9

Please sign in to comment.