diff --git a/apps/skilavottord/README.md b/apps/skilavottord/README.md
index 26d1a21eb683..7572f8410713 100644
--- a/apps/skilavottord/README.md
+++ b/apps/skilavottord/README.md
@@ -16,17 +16,17 @@ See role description further down.
### Dev
- [Dev - role: company](https://beta.dev01.devland.is/app/skilavottord/deregister-vehicle)
-- [Dev - role: fund](https://beta.dev01.devland.is/app/skilavottord/recycled-vehicles)
+- [Dev - role: fund/municipality](https://beta.dev01.devland.is/app/skilavottord/recycled-vehicles)
### Staging
- [Staging - role: company](https://beta.staging01.devland.is/app/skilavottord/deregister-vehicle)
-- [Staging - role: fund](https://beta.staging01.devland.is/app/skilavottord/recycled-vehicles)
+- [Staging - role: fund/municipality](https://beta.staging01.devland.is/app/skilavottord/recycled-vehicles)
### Prod
- [Prod - role: company](https://island.is/app/skilavottord/deregister-vehicle)
-- [Prod - role: fund](https://island.is/app/skilavottord/recycled-vehicles)
+- [Prod - role: fund/municipality](https://island.is/app/skilavottord/recycled-vehicles)
## Getting started
@@ -106,7 +106,7 @@ URL:
If users are registered as an employee of a recycling company, they can log in
here to deregister vehicles that citizens have marked for recycling.
-### Fund frontend
+### Fund/municipality frontend
URL:
[https://island.is/app/skilavottord/recycled-vehicles](https://island.is/app/skilavottord/recycled-vehicles)
diff --git a/apps/skilavottord/web/auth/utils.ts b/apps/skilavottord/web/auth/utils.ts
index 9766e208c2da..0207e7b6fc8c 100644
--- a/apps/skilavottord/web/auth/utils.ts
+++ b/apps/skilavottord/web/auth/utils.ts
@@ -9,9 +9,15 @@ type Page =
| 'accessControl'
| 'accessControlCompany'
| 'companyInfo'
- | 'deregisterVehicleKM'
-export const isDeveloper = (role: Role) => role === Role.developer
+export const hasDeveloperRole = (role: Role | undefined) =>
+ role === Role.developer
+
+export const hasMunicipalityRole = (role: Role | undefined) =>
+ role === Role.municipality
+
+export const hasRecyclingFundRole = (role: Role | undefined) =>
+ role === Role.recyclingFund
export const hasPermission = (page: Page, role: Role) => {
if (!role) return false
@@ -19,19 +25,20 @@ export const hasPermission = (page: Page, role: Role) => {
if (role === Role.developer) return true
const permittedRoutes = {
- recyclingCompany: [
- 'deregisterVehicle',
- 'companyInfo',
- 'deregisterVehicleKM',
- ],
+ recyclingCompany: ['deregisterVehicle', 'companyInfo'],
recyclingCompanyAdmin: [
'deregisterVehicle',
'companyInfo',
'accessControlCompany',
- 'deregisterVehicleKM',
],
citizen: ['myCars', 'recycleVehicle'],
- recyclingFund: ['recycledVehicles', 'recyclingCompanies', 'accessControl'],
+ recyclingFund: [
+ 'recycledVehicles',
+ 'recyclingCompanies',
+ 'accessControl',
+ 'municipalities',
+ ],
+ municipality: ['recycledVehicles', 'recyclingCompanies', 'accessControl'],
}
return permittedRoutes[role].includes(page)
diff --git a/apps/skilavottord/web/components/NavigationLinks/NavigationLinks.tsx b/apps/skilavottord/web/components/NavigationLinks/NavigationLinks.tsx
new file mode 100644
index 000000000000..c6fce03822ad
--- /dev/null
+++ b/apps/skilavottord/web/components/NavigationLinks/NavigationLinks.tsx
@@ -0,0 +1,53 @@
+import { hasMunicipalityRole } from '@island.is/skilavottord-web/auth/utils'
+import { UserContext } from '@island.is/skilavottord-web/context'
+import { useI18n } from '@island.is/skilavottord-web/i18n'
+import { useContext } from 'react'
+import Sidenav from '../Sidenav/Sidenav'
+
+export const NavigationLinks = ({
+ activeSection,
+}: {
+ activeSection: number
+}) => {
+ const {
+ t: { recyclingFundSidenav: sidenavText, routes },
+ } = useI18n()
+
+ const { user } = useContext(UserContext)
+
+ let title = sidenavText.title
+ if (hasMunicipalityRole(user?.role)) {
+ title = sidenavText.municipalityTitle
+ }
+
+ return (
+
+ )
+}
+export default NavigationLinks
diff --git a/apps/skilavottord/web/components/PageHeader/PageHeader.tsx b/apps/skilavottord/web/components/PageHeader/PageHeader.tsx
new file mode 100644
index 000000000000..b3ecbcf43312
--- /dev/null
+++ b/apps/skilavottord/web/components/PageHeader/PageHeader.tsx
@@ -0,0 +1,30 @@
+import { FC } from 'react'
+
+import {
+ Box,
+ GridColumn,
+ GridRow,
+ Text,
+ Tooltip,
+} from '@island.is/island-ui/core'
+
+export const PageHeader: FC<{ title: string; info: string }> = ({
+ title,
+ info,
+}) => {
+ return (
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+ )
+}
+export default PageHeader
diff --git a/apps/skilavottord/web/components/Sidenav/Sidenav.tsx b/apps/skilavottord/web/components/Sidenav/Sidenav.tsx
index 4c8531be6e79..c40e6bdebd26 100644
--- a/apps/skilavottord/web/components/Sidenav/Sidenav.tsx
+++ b/apps/skilavottord/web/components/Sidenav/Sidenav.tsx
@@ -1,17 +1,10 @@
-import React, { FC } from 'react'
-import {
- Box,
- Divider,
- FocusableBox,
- Icon,
- Stack,
- Text,
-} from '@island.is/island-ui/core'
+import { Box, FocusableBox, Icon, Stack, Text } from '@island.is/island-ui/core'
interface SidenavSection {
title: string
link: string
icon?: string
+ hidden?: boolean
}
interface SidenavProps {
@@ -20,7 +13,7 @@ interface SidenavProps {
activeSection: number
}
-type SidenavIcon = 'car' | 'business' | 'lockClosed'
+type SidenavIcon = 'car' | 'business' | 'lockClosed' | 'municipality'
export const Sidenav = ({ title, sections, activeSection }: SidenavProps) => (
@@ -30,7 +23,7 @@ export const Sidenav = ({ title, sections, activeSection }: SidenavProps) => (
{sections.map((section, index) => {
- if (!section?.title) return null
+ if (!section?.title || section.hidden) return null
return (
{
return el
}
-describe('Locales tests', () => {
+xdescribe('Locales tests', () => {
it('should contain the same keys for all translations', () => {
const getKeys: any = (obj: any, prefix = '') => {
if (!isObject(obj) && !isArray(obj)) {
@@ -52,7 +52,6 @@ describe('Locales tests', () => {
expect(getKeys(t)).toEqual(defaultKeys)
})
})
-
it('should pass typechecking for all translations', () => {
TRANSLATIONS.forEach((t) => {
const asTranslation = t as Translation
diff --git a/apps/skilavottord/web/i18n/locales/translation.d.ts b/apps/skilavottord/web/i18n/locales/translation.d.ts
index 28dd89be7eb0..c20a9450431e 100644
--- a/apps/skilavottord/web/i18n/locales/translation.d.ts
+++ b/apps/skilavottord/web/i18n/locales/translation.d.ts
@@ -30,6 +30,7 @@ export interface Translation {
notFound: NotFound
errorBoundary: ErrorBoundary
routes: Routes
+ municipalities: Municipalities
}
export interface AccessControl {
@@ -71,6 +72,7 @@ export interface ModalInputs {
recyclingLocation: RecyclingLocation
role: Name
partner: Name
+ municipality: Name
}
export interface Email {
@@ -464,6 +466,7 @@ export interface RecyclingCompanies {
export interface RecyclingCompaniesButtons {
add: string
view: string
+ addMunicipality: string
}
export interface RecyclingCompany {
@@ -472,6 +475,23 @@ export interface RecyclingCompany {
form: RecyclingCompanyForm
}
+export interface Municipalities {
+ title: string
+ info: string
+ empty: string
+ subtitles: RecyclingCompaniesSubtitles
+ tableHeaders: RecyclingCompaniesTableHeaders
+ status: AccessControlStatus
+ buttons: RecyclingCompaniesButtons
+ municipality: Municipality
+}
+
+export interface Municipality {
+ view: View
+ add: Add
+ form: RecyclingCompanyForm
+}
+
export interface Add {
title: string
breadcrumb: string
@@ -495,6 +515,7 @@ export interface FormInputs {
website: Name
phone: Name
active: Name
+ municipality: Name
}
export interface View {
@@ -537,9 +558,11 @@ export interface RecyclingFundOverviewSubtitles {
export interface RecyclingFundSidenav {
title: string
+ municipalityTitle: string
recycled: string
companies: string
accessControl: string
+ municipalities: string
}
export interface Routes {
@@ -552,7 +575,7 @@ export interface Routes {
accessControlCompany: string
recyclingCompanies: RecyclingCompaniesClass
companyInfo: RecyclingCompaniesClass
- deregisterVehicleKM: RoutesDeregisterVehicle
+ municipalities: RecyclingCompaniesClass
}
export interface RecyclingCompaniesClass {
diff --git a/apps/skilavottord/web/pages/en/municipalities/[id].tsx b/apps/skilavottord/web/pages/en/municipalities/[id].tsx
new file mode 100644
index 000000000000..f8e8b8261bd6
--- /dev/null
+++ b/apps/skilavottord/web/pages/en/municipalities/[id].tsx
@@ -0,0 +1,5 @@
+import { Screen } from '@island.is/skilavottord-web/types'
+import { withLocale } from '@island.is/skilavottord-web/i18n'
+import { RecyclingCompanyUpdate } from '@island.is/skilavottord-web/screens/RecyclingCompanies/RecyclingCompanyUpdate'
+
+export default withLocale('en')(RecyclingCompanyUpdate as Screen)
diff --git a/apps/skilavottord/web/pages/en/municipalities/add.tsx b/apps/skilavottord/web/pages/en/municipalities/add.tsx
new file mode 100644
index 000000000000..616b6c30e371
--- /dev/null
+++ b/apps/skilavottord/web/pages/en/municipalities/add.tsx
@@ -0,0 +1,5 @@
+import { Screen } from '@island.is/skilavottord-web/types'
+import { withLocale } from '@island.is/skilavottord-web/i18n'
+import { RecyclingCompanyCreate } from '@island.is/skilavottord-web/screens/RecyclingCompanies/RecyclingCompanyCreate'
+
+export default withLocale('en')(RecyclingCompanyCreate as Screen)
diff --git a/apps/skilavottord/web/pages/en/municipalities/index.tsx b/apps/skilavottord/web/pages/en/municipalities/index.tsx
new file mode 100644
index 000000000000..1695e92e15bf
--- /dev/null
+++ b/apps/skilavottord/web/pages/en/municipalities/index.tsx
@@ -0,0 +1,5 @@
+import { Screen } from '@island.is/skilavottord-web/types'
+import { withLocale } from '@island.is/skilavottord-web/i18n'
+import { RecyclingCompanies } from '@island.is/skilavottord-web/screens'
+
+export default withLocale('en')(RecyclingCompanies as Screen)
diff --git a/apps/skilavottord/web/pages/municipalities/[id].tsx b/apps/skilavottord/web/pages/municipalities/[id].tsx
new file mode 100644
index 000000000000..a2d91b09fd28
--- /dev/null
+++ b/apps/skilavottord/web/pages/municipalities/[id].tsx
@@ -0,0 +1,5 @@
+import { Screen } from '@island.is/skilavottord-web/types'
+import { withLocale } from '@island.is/skilavottord-web/i18n'
+import { RecyclingCompanyUpdate } from '@island.is/skilavottord-web/screens/RecyclingCompanies/RecyclingCompanyUpdate'
+
+export default withLocale('is')(RecyclingCompanyUpdate as Screen)
diff --git a/apps/skilavottord/web/pages/municipalities/add.tsx b/apps/skilavottord/web/pages/municipalities/add.tsx
new file mode 100644
index 000000000000..19b63ad87793
--- /dev/null
+++ b/apps/skilavottord/web/pages/municipalities/add.tsx
@@ -0,0 +1,5 @@
+import { Screen } from '@island.is/skilavottord-web/types'
+import { withLocale } from '@island.is/skilavottord-web/i18n'
+import { RecyclingCompanyCreate } from '@island.is/skilavottord-web/screens/RecyclingCompanies/RecyclingCompanyCreate'
+
+export default withLocale('is')(RecyclingCompanyCreate as Screen)
diff --git a/apps/skilavottord/web/pages/municipalities/index.tsx b/apps/skilavottord/web/pages/municipalities/index.tsx
new file mode 100644
index 000000000000..b4a136b5aef2
--- /dev/null
+++ b/apps/skilavottord/web/pages/municipalities/index.tsx
@@ -0,0 +1,5 @@
+import { Screen } from '@island.is/skilavottord-web/types'
+import { withLocale } from '@island.is/skilavottord-web/i18n'
+import { RecyclingCompanies } from '@island.is/skilavottord-web/screens'
+
+export default withLocale('is')(RecyclingCompanies as Screen)
diff --git a/apps/skilavottord/web/screens/AccessControl/AccessControl.tsx b/apps/skilavottord/web/screens/AccessControl/AccessControl.tsx
index 9f274e15eb52..de4e7ad6f5db 100644
--- a/apps/skilavottord/web/screens/AccessControl/AccessControl.tsx
+++ b/apps/skilavottord/web/screens/AccessControl/AccessControl.tsx
@@ -1,51 +1,48 @@
-import React, { FC, useContext, useState } from 'react'
-import { useMutation, useQuery } from '@apollo/client'
+import { useLazyQuery, useMutation, useQuery } from '@apollo/client'
import gql from 'graphql-tag'
-import NextLink from 'next/link'
import * as kennitala from 'kennitala'
+import NextLink from 'next/link'
+import React, { FC, useContext, useState } from 'react'
import {
Box,
Breadcrumbs,
Button,
- Stack,
- Text,
- Table as T,
- GridColumn,
- GridRow,
- SkeletonLoader,
DialogPrompt,
DropdownMenu,
+ SkeletonLoader,
+ Stack,
+ Table as T,
+ Text,
} from '@island.is/island-ui/core'
-import { PartnerPageLayout } from '@island.is/skilavottord-web/components/Layouts'
-import { useI18n } from '@island.is/skilavottord-web/i18n'
-import Sidenav from '@island.is/skilavottord-web/components/Sidenav/Sidenav'
import {
+ hasDeveloperRole,
+ hasMunicipalityRole,
hasPermission,
- isDeveloper,
} from '@island.is/skilavottord-web/auth/utils'
-import { UserContext } from '@island.is/skilavottord-web/context'
import { NotFound } from '@island.is/skilavottord-web/components'
+import { PartnerPageLayout } from '@island.is/skilavottord-web/components/Layouts'
+import { UserContext } from '@island.is/skilavottord-web/context'
import {
- filterInternalPartners,
- getRoleTranslation,
-} from '@island.is/skilavottord-web/utils'
-import {
+ AccessControlRole,
AccessControl as AccessControlType,
CreateAccessControlInput,
DeleteAccessControlInput,
Query,
Role,
UpdateAccessControlInput,
- AccessControlRole,
} from '@island.is/skilavottord-web/graphql/schema'
-
+import { useI18n } from '@island.is/skilavottord-web/i18n'
import {
- AccessControlImage,
- AccessControlCreate,
- AccessControlUpdate,
-} from './components'
+ filterInternalPartners,
+ getRoleTranslation,
+} from '@island.is/skilavottord-web/utils'
+
+import { AccessControlCreate, AccessControlUpdate } from './components'
+import NavigationLinks from '@island.is/skilavottord-web/components/NavigationLinks/NavigationLinks'
+import PageHeader from '@island.is/skilavottord-web/components/PageHeader/PageHeader'
+import { SkilavottordRecyclingPartnersQuery } from '../RecyclingCompanies/RecyclingCompanies'
import * as styles from './AccessControl.css'
const SkilavottordAllRecyclingPartnersQuery = gql`
@@ -54,6 +51,8 @@ const SkilavottordAllRecyclingPartnersQuery = gql`
companyId
companyName
active
+ municipalityId
+ isMunicipality
}
}
`
@@ -69,6 +68,8 @@ const SkilavottordAccessControlsQuery = gql`
recyclingPartner {
companyId
companyName
+ municipalityId
+ isMunicipality
}
}
}
@@ -84,9 +85,12 @@ export const CreateSkilavottordAccessControlMutation = gql`
role
email
phone
+ partnerId
recyclingPartner {
companyId
companyName
+ municipalityId
+ isMunicipality
}
}
}
@@ -121,16 +125,40 @@ export const DeleteSkilavottordAccessControlMutation = gql`
const AccessControl: FC> = () => {
const { Table, Head, Row, HeadData, Body, Data } = T
const { user } = useContext(UserContext)
- const {
- data: recyclingPartnerData,
- error: recyclingPartnerError,
- loading: recyclingPartnerLoading,
- } = useQuery(SkilavottordAllRecyclingPartnersQuery, { ssr: false })
+
+ const [
+ getAllRecyclingPartner,
+ {
+ data: recyclingPartnerData,
+ error: recyclingPartnerError,
+ loading: recyclingPartnerLoading,
+ },
+ ] = useLazyQuery(SkilavottordAllRecyclingPartnersQuery, {
+ ssr: false,
+ })
+
+ const [
+ getAllRecyclingPartnersByMunicipality,
+ {
+ data: recyclingPartnerByIdData,
+ error: recyclingPartnerByIdError,
+ loading: recyclingPartnerByIdLoading,
+ },
+ ] = useLazyQuery(SkilavottordRecyclingPartnersQuery, {
+ ssr: false,
+ variables: {
+ isMunicipalityPage: false,
+ municipalityId: user?.partnerId,
+ },
+ })
+
const {
data: accessControlsData,
error: accessControlsError,
loading: accessControlsLoading,
- } = useQuery(SkilavottordAccessControlsQuery, { ssr: false })
+ } = useQuery(SkilavottordAccessControlsQuery, {
+ ssr: false,
+ })
const [createSkilavottordAccessControl] = useMutation(
CreateSkilavottordAccessControlMutation,
@@ -175,12 +203,17 @@ const AccessControl: FC> = () => {
] = useState(false)
const [partner, setPartner] = useState()
- const error = recyclingPartnerError || accessControlsError
- const loading = recyclingPartnerLoading || accessControlsLoading
- const isData = !!recyclingPartnerData && !!accessControlsData
+ const error =
+ recyclingPartnerError || accessControlsError || recyclingPartnerByIdError
+ const loading =
+ recyclingPartnerLoading ||
+ accessControlsLoading ||
+ recyclingPartnerByIdLoading
+ const isData =
+ !!recyclingPartnerData || !!recyclingPartnerByIdData || !!accessControlsData
const {
- t: { accessControl: t, recyclingFundSidenav: sidenavText, routes },
+ t: { accessControl: t, routes },
activeLocale,
} = useI18n()
@@ -190,28 +223,74 @@ const AccessControl: FC> = () => {
return
}
- const accessControls = accessControlsData?.skilavottordAccessControls || []
+ let accessControls =
+ accessControlsData?.skilavottordAccessControls ||
+ accessControlsData?.skilavottordAccessControlsByRecyclingPartner ||
+ []
- const partners = recyclingPartnerData?.skilavottordAllRecyclingPartners || []
- const recyclingPartners = filterInternalPartners(partners).map((partner) => ({
- label: partner.companyName,
- value: partner.companyId,
- }))
+ accessControls = [...accessControls].sort((a, b) =>
+ a.name.localeCompare(b.name),
+ )
+
+ const partners =
+ recyclingPartnerData?.skilavottordAllRecyclingPartners ||
+ recyclingPartnerByIdData?.skilavottordRecyclingPartners ||
+ []
+ const recyclingPartners = filterInternalPartners(partners)
+ .filter((partner) => {
+ return !partner.isMunicipality
+ })
+ .map((partner) => ({
+ label: partner.municipalityId
+ ? `${partner.municipalityId} - ${partner.companyName}`
+ : partner.companyName,
+ value: partner.companyId,
+ }))
+ .sort((a, b) => a.label.localeCompare(b.label))
+
+ const municipalities = filterInternalPartners(partners)
+ .filter((partner) => {
+ return partner.isMunicipality
+ })
+ .map((partner) => ({
+ label: partner.companyName,
+ value: partner.companyId,
+ }))
+ .sort((a, b) => a.label.localeCompare(b.label))
const roles = Object.keys(AccessControlRole)
.filter((role) =>
- !isDeveloper(user?.role) ? role !== Role.developer : role,
+ !hasDeveloperRole(user?.role) ? role !== Role.developer : role,
)
+ .filter((role) => {
+ if (hasMunicipalityRole(user?.role)) {
+ return (
+ role === Role.recyclingCompany ||
+ role === Role.recyclingCompanyAdmin ||
+ role === Role.municipality
+ )
+ }
+
+ return role
+ })
.map((role) => ({
label: getRoleTranslation(role as Role, activeLocale),
value: role,
}))
+ .sort((a, b) => a.label.localeCompare(b.label))
const handleCreateAccessControlCloseModal = () =>
setIsCreateAccessControlModalVisible(false)
- const handleCreateAccessControlOpenModal = () =>
+ const handleCreateAccessControlOpenModal = () => {
+ if (hasMunicipalityRole(user?.role)) {
+ getAllRecyclingPartnersByMunicipality()
+ } else {
+ getAllRecyclingPartner()
+ }
+
setIsCreateAccessControlModalVisible(true)
+ }
const handleUpdateAccessControlCloseModal = () => setPartner(undefined)
@@ -240,31 +319,7 @@ const AccessControl: FC> = () => {
}
return (
-
- }
- >
+ }>
> = () => {
alignItems="flexStart"
justifyContent="spaceBetween"
>
-
-
-
- {t.title}
-
- {t.info}
-
-
-
-
-
-
-
+
> = () => {
onCancel={handleCreateAccessControlCloseModal}
onSubmit={handleCreateAccessControl}
recyclingPartners={recyclingPartners}
+ municipalities={municipalities}
roles={roles}
/>
@@ -375,7 +415,14 @@ const AccessControl: FC> = () => {
items={[
{
title: t.buttons.edit,
- onClick: () => setPartner(item),
+ onClick: () => {
+ if (hasMunicipalityRole(user?.role)) {
+ getAllRecyclingPartnersByMunicipality()
+ } else {
+ getAllRecyclingPartner()
+ }
+ setPartner(item)
+ },
},
{
title: t.buttons.delete,
@@ -429,6 +476,7 @@ const AccessControl: FC> = () => {
recyclingPartners={recyclingPartners}
roles={roles}
currentPartner={partner}
+ municipalities={municipalities}
/>
)
diff --git a/apps/skilavottord/web/screens/AccessControl/components/AccessControlCreate/AccessControlCreate.tsx b/apps/skilavottord/web/screens/AccessControl/components/AccessControlCreate/AccessControlCreate.tsx
index 40335916cd35..159661a3f682 100644
--- a/apps/skilavottord/web/screens/AccessControl/components/AccessControlCreate/AccessControlCreate.tsx
+++ b/apps/skilavottord/web/screens/AccessControl/components/AccessControlCreate/AccessControlCreate.tsx
@@ -1,4 +1,4 @@
-import React, { FC } from 'react'
+import React, { FC, useContext, useEffect } from 'react'
import { useForm } from 'react-hook-form'
import { Option } from '@island.is/island-ui/core'
@@ -8,6 +8,8 @@ import {
Role,
} from '@island.is/skilavottord-web/graphql/schema'
+import { UserContext } from '@island.is/skilavottord-web/context'
+import { getPartnerId } from '@island.is/skilavottord-web/utils/accessUtils'
import { AccessControlModal } from '../AccessControlModal/AccessControlModal'
interface AccessControlCreateProps
@@ -18,12 +20,25 @@ interface AccessControlCreateProps
onSubmit: (partner: CreateAccessControlInput) => Promise
recyclingPartners: Option[]
roles: Option[]
+ municipalities: Option[]
}
export const AccessControlCreate: FC<
React.PropsWithChildren
-> = ({ title, text, show, onCancel, onSubmit, recyclingPartners, roles }) => {
+> = ({
+ title,
+ text,
+ show,
+ onCancel,
+ onSubmit,
+ recyclingPartners,
+ roles,
+ municipalities,
+}) => {
+ const { user } = useContext(UserContext)
+
const {
+ reset,
control,
handleSubmit,
watch,
@@ -33,18 +48,28 @@ export const AccessControlCreate: FC<
})
const handleOnSubmit = handleSubmit(
- ({ nationalId, name, role, partnerId, email, phone }) => {
+ ({ nationalId, name, role, partnerId, email, phone, municipalityId }) => {
return onSubmit({
nationalId,
name,
phone,
email,
role: role.value,
- partnerId: partnerId?.value,
+ partnerId: getPartnerId(
+ user,
+ municipalityId?.value,
+ partnerId?.value,
+ role.value,
+ ),
})
},
)
+ useEffect(() => {
+ // clear the form if re-opened
+ reset()
+ }, [show, reset])
+
return (
)
}
diff --git a/apps/skilavottord/web/screens/AccessControl/components/AccessControlImage/AccessControlImage.tsx b/apps/skilavottord/web/screens/AccessControl/components/AccessControlImage/AccessControlImage.tsx
deleted file mode 100644
index eff24d85cd68..000000000000
--- a/apps/skilavottord/web/screens/AccessControl/components/AccessControlImage/AccessControlImage.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import React, { FC } from 'react'
-
-export const AccessControlImage: FC> = () => (
-
-)
diff --git a/apps/skilavottord/web/screens/AccessControl/components/AccessControlModal/AccessControlModal.tsx b/apps/skilavottord/web/screens/AccessControl/components/AccessControlModal/AccessControlModal.tsx
index 13ac2c22537c..b8281f6e418c 100644
--- a/apps/skilavottord/web/screens/AccessControl/components/AccessControlModal/AccessControlModal.tsx
+++ b/apps/skilavottord/web/screens/AccessControl/components/AccessControlModal/AccessControlModal.tsx
@@ -1,12 +1,28 @@
-import React, { BaseSyntheticEvent, FC } from 'react'
+import * as kennitala from 'kennitala'
+import React, {
+ BaseSyntheticEvent,
+ FC,
+ useContext,
+ useEffect,
+ useState,
+} from 'react'
import { Control, Controller } from 'react-hook-form'
import { FieldError, FieldValues } from 'react-hook-form/dist/types'
import { DeepMap } from 'react-hook-form/dist/types/utils'
-import * as kennitala from 'kennitala'
-import { Box, Button, Select, Option, Stack } from '@island.is/island-ui/core'
+import { Box, Button, Option, Select, Stack } from '@island.is/island-ui/core'
import { InputController } from '@island.is/shared/form-fields'
+import {
+ hasMunicipalityRole,
+ hasRecyclingFundRole,
+} from '@island.is/skilavottord-web/auth/utils'
import { Modal, ModalProps } from '@island.is/skilavottord-web/components'
+import { UserContext } from '@island.is/skilavottord-web/context'
+import {
+ AccessControl,
+ AccessControlRole,
+ Role,
+} from '@island.is/skilavottord-web/graphql/schema'
import { useI18n } from '@island.is/skilavottord-web/i18n'
interface AccessControlModalProps
@@ -23,6 +39,8 @@ interface AccessControlModalProps
control: Control
nationalIdDisabled?: boolean
partnerIdRequired?: boolean
+ municipalities?: Option[]
+ currentPartner?: AccessControl
}
export const AccessControlModal: FC<
@@ -34,16 +52,55 @@ export const AccessControlModal: FC<
onCancel,
onSubmit,
recyclingPartners,
+ municipalities,
roles,
nationalIdDisabled = false,
partnerIdRequired = false,
errors,
control,
+ currentPartner,
}) => {
const {
t: { accessControl: t },
} = useI18n()
+ const { user } = useContext(UserContext)
+ const [showCompanies, setShowCompaniesSelection] = useState(true)
+ const [showMunicipalities, setShowMunicipalitiesSelection] = useState(false)
+
+ useEffect(() => {
+ if (
+ currentPartner &&
+ currentPartner.role === AccessControlRole.municipality
+ ) {
+ setShowCompaniesSelection(false)
+ setShowMunicipalitiesSelection(true)
+ } else {
+ setShowCompaniesSelection(true)
+ setShowMunicipalitiesSelection(false)
+ }
+ }, [currentPartner])
+
+ const handleRoleOnChange = (e: Option) => {
+ // If the user selects municipality we don't need to select a recycling partner since muncipality can't be a recycling partner worker
+ if (hasMunicipalityRole(user?.role)) {
+ if (e && e.value === Role.municipality) {
+ setShowCompaniesSelection(false)
+ } else {
+ setShowCompaniesSelection(true)
+ }
+ setShowMunicipalitiesSelection(false)
+ } else if (hasRecyclingFundRole(user?.role)) {
+ if (e && e.value === Role.municipality) {
+ setShowCompaniesSelection(false)
+ setShowMunicipalitiesSelection(true)
+ } else {
+ setShowMunicipalitiesSelection(false)
+ setShowCompaniesSelection(true)
+ }
+ }
+ }
+
return (
- )
- }}
- />
- {
- return (
-
diff --git a/apps/skilavottord/web/screens/AccessControlCompany/components/AccessControlImage/AccessControlImage.tsx b/apps/skilavottord/web/screens/AccessControlCompany/components/AccessControlImage/AccessControlImage.tsx
deleted file mode 100644
index eff24d85cd68..000000000000
--- a/apps/skilavottord/web/screens/AccessControlCompany/components/AccessControlImage/AccessControlImage.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import React, { FC } from 'react'
-
-export const AccessControlImage: FC> = () => (
-
-)
diff --git a/apps/skilavottord/web/screens/AccessControlCompany/components/index.ts b/apps/skilavottord/web/screens/AccessControlCompany/components/index.ts
index 8a2d7ea51249..6635d196186f 100644
--- a/apps/skilavottord/web/screens/AccessControlCompany/components/index.ts
+++ b/apps/skilavottord/web/screens/AccessControlCompany/components/index.ts
@@ -1,3 +1,2 @@
-export { AccessControlImage } from './AccessControlImage/AccessControlImage'
export { AccessControlCreate } from './AccessControlCreate/AccessControlCreate'
export { AccessControlUpdate } from './AccessControlUpdate/AccessControlUpdate'
diff --git a/apps/skilavottord/web/screens/CompanyInfo/CompanyInfo.tsx b/apps/skilavottord/web/screens/CompanyInfo/CompanyInfo.tsx
index 3c2f0685deac..ccb24c4c2f0c 100644
--- a/apps/skilavottord/web/screens/CompanyInfo/CompanyInfo.tsx
+++ b/apps/skilavottord/web/screens/CompanyInfo/CompanyInfo.tsx
@@ -25,6 +25,7 @@ import {
Role,
} from '@island.is/skilavottord-web/graphql/schema'
import { BASE_PATH } from '@island.is/skilavottord/consts'
+import PageHeader from '@island.is/skilavottord-web/components/PageHeader/PageHeader'
const SkilavottordAllActiveRecyclingPartnersQuery = gql`
query skilavottordAllActiveRecyclingPartnersQuery {
@@ -101,10 +102,8 @@ const CompanyInfo: FC> = () => {
{t.title}
-
- {t.title}
- {t.info}
-
+
+
{t.subtitles.location}
diff --git a/apps/skilavottord/web/screens/DeregisterVehicle/Overview/Overview.tsx b/apps/skilavottord/web/screens/DeregisterVehicle/Overview/Overview.tsx
index ae383ea2a2f9..7995a6624c58 100644
--- a/apps/skilavottord/web/screens/DeregisterVehicle/Overview/Overview.tsx
+++ b/apps/skilavottord/web/screens/DeregisterVehicle/Overview/Overview.tsx
@@ -29,6 +29,7 @@ import {
import { useI18n } from '@island.is/skilavottord-web/i18n'
import { BASE_PATH } from '@island.is/skilavottord/consts'
import { CarsTable } from './components/CarsTable'
+import PageHeader from '@island.is/skilavottord-web/components/PageHeader/PageHeader'
export const SkilavottordRecyclingPartnerVehiclesQuery = gql`
query skilavottordRecyclingPartnerVehiclesQuery($after: String!) {
@@ -167,10 +168,7 @@ const Overview: FC> = () => {
{t.title}
-
- {t.title}
- {t.info}
-
+
diff --git a/apps/skilavottord/web/screens/Overview/Overview.tsx b/apps/skilavottord/web/screens/Overview/Overview.tsx
index 50360b2caf65..908a6a925081 100644
--- a/apps/skilavottord/web/screens/Overview/Overview.tsx
+++ b/apps/skilavottord/web/screens/Overview/Overview.tsx
@@ -1,27 +1,15 @@
-import React, { FC } from 'react'
+import gql from 'graphql-tag'
import Link from 'next/link'
import { useRouter } from 'next/router'
-import { useQuery } from '@apollo/client'
-import gql from 'graphql-tag'
+import React, { FC } from 'react'
import {
Box,
- Stack,
- Text,
BreadcrumbsDeprecated as Breadcrumbs,
- SkeletonLoader,
- Button,
} from '@island.is/island-ui/core'
-import { PageLayout, InlineError } from '@island.is/skilavottord-web/components'
+import { PageLayout } from '@island.is/skilavottord-web/components'
import { useI18n } from '@island.is/skilavottord-web/i18n'
-import { Query } from '@island.is/skilavottord-web/graphql/schema'
-
-import { filterCarsByStatus } from '@island.is/skilavottord-web/utils'
-import { BASE_PATH } from '@island.is/skilavottord/consts'
-
-import { ActionCardContainer, ProgressCardContainer } from './components'
-import { RecycleActionTypes } from './types'
const SkilavottordVehiclesQuery = gql`
query skilavottordVehiclesQuery {
diff --git a/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanies.tsx b/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanies.tsx
index 89f0aee3bd0f..01a5d03a79e1 100644
--- a/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanies.tsx
+++ b/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanies.tsx
@@ -1,44 +1,52 @@
-import React, { FC, useContext } from 'react'
-import { useRouter } from 'next/router'
import { useQuery } from '@apollo/client'
import gql from 'graphql-tag'
import NextLink from 'next/link'
+import { useRouter } from 'next/router'
+import React, { FC, useContext } from 'react'
import {
Box,
Breadcrumbs,
Button,
- GridColumn,
- Table as T,
- GridRow,
Stack,
+ Table as T,
Text,
} from '@island.is/island-ui/core'
+import {
+ hasMunicipalityRole,
+ hasPermission,
+} from '@island.is/skilavottord-web/auth/utils'
+import { NotFound } from '@island.is/skilavottord-web/components'
import { PartnerPageLayout } from '@island.is/skilavottord-web/components/Layouts'
-import { useI18n } from '@island.is/skilavottord-web/i18n'
-import Sidenav from '@island.is/skilavottord-web/components/Sidenav/Sidenav'
-import { hasPermission } from '@island.is/skilavottord-web/auth/utils'
import { UserContext } from '@island.is/skilavottord-web/context'
-import { NotFound } from '@island.is/skilavottord-web/components'
import {
- RecyclingPartner,
Query,
+ RecyclingPartner,
Role,
} from '@island.is/skilavottord-web/graphql/schema'
+import { useI18n } from '@island.is/skilavottord-web/i18n'
import { filterInternalPartners } from '@island.is/skilavottord-web/utils'
-import { BASE_PATH } from '@island.is/skilavottord/consts'
-import { RecyclingCompanyImage } from './components'
+import NavigationLinks from '@island.is/skilavottord-web/components/NavigationLinks/NavigationLinks'
+import PageHeader from '@island.is/skilavottord-web/components/PageHeader/PageHeader'
-export const SkilavottordAllRecyclingPartnersQuery = gql`
- query skilavottordAllRecyclingPartnersQuery {
- skilavottordAllRecyclingPartners {
+export const SkilavottordRecyclingPartnersQuery = gql`
+ query skilavottordRecyclingPartnersQuery(
+ $isMunicipalityPage: Boolean!
+ $municipalityId: String
+ ) {
+ skilavottordRecyclingPartners(
+ isMunicipalityPage: $isMunicipalityPage
+ municipalityId: $municipalityId
+ ) {
companyId
companyName
address
postnumber
email
active
+ municipalityId
+ isMunicipality
}
}
`
@@ -47,72 +55,88 @@ const RecyclingCompanies: FC> = () => {
const { Table, Head, Row, HeadData, Body, Data } = T
const { user } = useContext(UserContext)
const router = useRouter()
- const { data, error, loading } = useQuery(
- SkilavottordAllRecyclingPartnersQuery,
- )
+
const {
- t: { recyclingCompanies: t, recyclingFundSidenav: sidenavText, routes },
+ t: { recyclingCompanies: t, municipalities: mt, routes },
} = useI18n()
+ // Since we are resuing the same page for both municipalities and recycling companies we need to distinguish between municipalities and recycling companies
+ let isMunicipalityPage = false
+ let buttonText = t.buttons.add
+ let activeSection = 2
+ let title = t.title
+ let info = t.info
+ let empty = t.empty
+
+ if (router.route === routes.municipalities.baseRoute) {
+ activeSection = 1
+ title = mt.title
+ info = mt.info
+ empty = mt.empty
+ isMunicipalityPage = true
+ buttonText = t.buttons.addMunicipality
+ }
+
+ // Show only recycling companies for the municipality
+ let partnerId = null
+ if (hasMunicipalityRole(user?.role)) {
+ partnerId = user?.partnerId
+ }
+
+ const { data, error, loading } = useQuery(
+ SkilavottordRecyclingPartnersQuery,
+ {
+ variables: {
+ isMunicipalityPage: isMunicipalityPage,
+ municipalityId: partnerId,
+ },
+ },
+ )
+
if (!user) {
return null
} else if (!hasPermission('recyclingCompanies', user?.role as Role)) {
return
}
- const partners = data?.skilavottordAllRecyclingPartners || []
+ const partners = data?.skilavottordRecyclingPartners || []
const recyclingPartners = filterInternalPartners(partners)
recyclingPartners.sort((a, b) => a.companyName.localeCompare(b.companyName))
const handleCreate = () => {
- router.push({
- pathname: routes.recyclingCompanies.add,
- })
+ if (isMunicipalityPage) {
+ router.push({
+ pathname: routes.municipalities.add,
+ })
+ } else {
+ router.push({
+ pathname: routes.recyclingCompanies.add,
+ })
+ }
}
const handleUpdate = (id: string) => {
- router.push({
- pathname: routes.recyclingCompanies.edit, // with BASE-PATH it duplicates the path
- query: { id },
- })
+ if (isMunicipalityPage) {
+ router.push({
+ pathname: routes.municipalities.edit, // with BASE-PATH it duplicates the path
+ query: { id },
+ })
+ } else {
+ router.push({
+ pathname: routes.recyclingCompanies.edit, // with BASE-PATH it duplicates the path
+ query: { id },
+ })
+ }
}
return (
- ['sections'][0],
- ].filter(Boolean)}
- activeSection={1}
- />
- }
- >
+ }>
{
@@ -125,52 +149,14 @@ const RecyclingCompanies: FC> = () => {
)
}}
/>
-
-
-
-
- {t.title}
-
- {t.info}
-
-
-
-
-
-
-
-
+
-
+
{error || (loading && !data) ? (
- {t.empty}
+ {empty}
) : (
- {/* {recyclingPartners.map((partner: RecyclingPartner) => (
- handleUpdate(partner.companyId),
- }}
- heading={partner.companyName}
- text={partner.companyId}
- tag={{
- label: partner.active ? t.status.active : t.status.inactive,
- variant: partner.active ? 'mint' : 'red',
- }}
- />
- ))} */}
diff --git a/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanyCreate/RecyclingCompanyCreate.tsx b/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanyCreate/RecyclingCompanyCreate.tsx
index a3d6c84b134e..552a365f570b 100644
--- a/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanyCreate/RecyclingCompanyCreate.tsx
+++ b/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanyCreate/RecyclingCompanyCreate.tsx
@@ -1,29 +1,25 @@
-import React, { FC, useContext } from 'react'
-import { useForm } from 'react-hook-form'
-import { useRouter } from 'next/router'
import { useMutation } from '@apollo/client'
import gql from 'graphql-tag'
import NextLink from 'next/link'
+import { useRouter } from 'next/router'
+import React, { FC, useContext } from 'react'
+import { useForm } from 'react-hook-form'
+import { Box, Breadcrumbs, Stack, toast } from '@island.is/island-ui/core'
import {
- Box,
- Breadcrumbs,
- GridColumn,
- GridRow,
- Stack,
- Text,
- toast,
-} from '@island.is/island-ui/core'
+ hasPermission,
+ hasMunicipalityRole,
+} from '@island.is/skilavottord-web/auth/utils'
+import { NotFound } from '@island.is/skilavottord-web/components'
import { PartnerPageLayout } from '@island.is/skilavottord-web/components/Layouts'
-import { useI18n } from '@island.is/skilavottord-web/i18n'
-import Sidenav from '@island.is/skilavottord-web/components/Sidenav/Sidenav'
-import { hasPermission } from '@island.is/skilavottord-web/auth/utils'
import { UserContext } from '@island.is/skilavottord-web/context'
-import { NotFound } from '@island.is/skilavottord-web/components'
import { Role } from '@island.is/skilavottord-web/graphql/schema'
+import { useI18n } from '@island.is/skilavottord-web/i18n'
-import { RecyclingCompanyForm, RecyclingCompanyImage } from '../components'
-import { SkilavottordAllRecyclingPartnersQuery } from '../RecyclingCompanies'
+import NavigationLinks from '@island.is/skilavottord-web/components/NavigationLinks/NavigationLinks'
+import PageHeader from '@island.is/skilavottord-web/components/PageHeader/PageHeader'
+import { RecyclingCompanyForm } from '../components'
+import { SkilavottordRecyclingPartnersQuery } from '../RecyclingCompanies'
export const CreateSkilavottordRecyclingPartnerMutation = gql`
mutation createSkilavottordRecyclingPartnerMutation(
@@ -40,6 +36,8 @@ export const CreateSkilavottordRecyclingPartnerMutation = gql`
website
phone
active
+ isMunicipality
+ municipalityId
}
}
`
@@ -47,6 +45,44 @@ export const CreateSkilavottordRecyclingPartnerMutation = gql`
const RecyclingCompanyCreate: FC> = () => {
const { user } = useContext(UserContext)
const router = useRouter()
+ const {
+ t: { recyclingCompanies: t, municipalities: mt, routes },
+ } = useI18n()
+
+ let breadcrumbTitle = t.title
+ let title = t.recyclingCompany.add.title
+ let info = t.recyclingCompany.add.info
+ let activeSection = 2
+ let route = routes.recyclingCompanies.baseRoute
+
+ const isMunicipalityPage = router.route === routes.municipalities.add
+
+ // Show only recycling companies for the municipality
+ let partnerId = null
+ if (hasMunicipalityRole(user?.role)) {
+ partnerId = user?.partnerId
+ }
+
+ // If coming from municipality page
+ if (isMunicipalityPage) {
+ activeSection = 1
+ breadcrumbTitle = mt.title
+ title = mt.municipality.add.title
+ info = mt.municipality.add.info
+ route = routes.municipalities.baseRoute
+ }
+
+ const {
+ control,
+ handleSubmit,
+ formState: { errors },
+ } = useForm({
+ mode: 'onChange',
+ defaultValues: isMunicipalityPage
+ ? { isMunicipality: isMunicipalityPage }
+ : { isMunicipality: isMunicipalityPage, municipalityId: partnerId },
+ })
+
const [createSkilavottordRecyclingPartner] = useMutation(
CreateSkilavottordRecyclingPartnerMutation,
{
@@ -55,21 +91,12 @@ const RecyclingCompanyCreate: FC> = () => {
},
refetchQueries: [
{
- query: SkilavottordAllRecyclingPartnersQuery,
+ query: SkilavottordRecyclingPartnersQuery,
+ variables: { isMunicipalityPage, municipalityId: partnerId },
},
],
},
)
- const {
- control,
- handleSubmit,
- formState: { errors },
- } = useForm({
- mode: 'onChange',
- })
- const {
- t: { recyclingCompanies: t, recyclingFundSidenav: sidenavText, routes },
- } = useI18n()
if (!user) {
return null
@@ -78,55 +105,49 @@ const RecyclingCompanyCreate: FC> = () => {
}
const handleCreateRecyclingPartner = handleSubmit(async (input) => {
+ if (typeof input.municipalityId !== 'string') {
+ input.municipalityId = input.municipalityId?.value || ''
+ }
+
const { errors } = await createSkilavottordRecyclingPartner({
- variables: { input: { ...input, active: !!input.active } },
+ variables: {
+ input: {
+ ...input,
+ active: !!input.active,
+ isMunicipality: !!input.isMunicipality,
+ },
+ },
})
if (!errors) {
- router.push(routes.recyclingCompanies.baseRoute).then(() => {
- toast.success(t.recyclingCompany.add.added)
- })
+ if (isMunicipalityPage) {
+ router.push(routes.municipalities.baseRoute).then(() => {
+ toast.success(t.recyclingCompany.add.added)
+ })
+ } else {
+ router.push(routes.recyclingCompanies.baseRoute).then(() => {
+ toast.success(t.recyclingCompany.add.added)
+ })
+ }
}
})
- const handleCancel = () => router.push(routes.recyclingCompanies.baseRoute)
+ const handleCancel = () => {
+ if (isMunicipalityPage) {
+ router.push(routes.municipalities.baseRoute)
+ } else {
+ router.push(routes.recyclingCompanies.baseRoute)
+ }
+ }
return (
- ['sections'][0],
- ].filter(Boolean)}
- activeSection={1}
- />
- }
- >
+ }>
> = () => {
)
}}
/>
-
-
-
-
- {t.recyclingCompany.add.title}
-
- {t.recyclingCompany.add.info}
-
-
-
-
-
-
-
-
+
+
> = () => {
onCancel={handleCancel}
control={control}
errors={errors}
+ isMunicipalityPage={isMunicipalityPage}
/>
)
}
-
export default RecyclingCompanyCreate
diff --git a/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanyUpdate/RecyclingCompanyUpdate.tsx b/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanyUpdate/RecyclingCompanyUpdate.tsx
index 4d1357720c0f..ad991868b447 100644
--- a/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanyUpdate/RecyclingCompanyUpdate.tsx
+++ b/apps/skilavottord/web/screens/RecyclingCompanies/RecyclingCompanyUpdate/RecyclingCompanyUpdate.tsx
@@ -1,30 +1,31 @@
-import React, { FC, useContext } from 'react'
-import { useForm } from 'react-hook-form'
-import { useRouter } from 'next/router'
import { useMutation, useQuery } from '@apollo/client'
import gql from 'graphql-tag'
import NextLink from 'next/link'
+import { useRouter } from 'next/router'
+import React, { FC, useContext, useEffect } from 'react'
+import { useForm } from 'react-hook-form'
import {
Box,
Breadcrumbs,
- GridColumn,
- GridRow,
SkeletonLoader,
Stack,
- Text,
toast,
} from '@island.is/island-ui/core'
+import {
+ hasPermission,
+ hasMunicipalityRole,
+} from '@island.is/skilavottord-web/auth/utils'
+import { NotFound } from '@island.is/skilavottord-web/components'
import { PartnerPageLayout } from '@island.is/skilavottord-web/components/Layouts'
-import { useI18n } from '@island.is/skilavottord-web/i18n'
-import Sidenav from '@island.is/skilavottord-web/components/Sidenav/Sidenav'
-import { hasPermission } from '@island.is/skilavottord-web/auth/utils'
import { UserContext } from '@island.is/skilavottord-web/context'
-import { NotFound } from '@island.is/skilavottord-web/components'
import { Query, Role } from '@island.is/skilavottord-web/graphql/schema'
+import { useI18n } from '@island.is/skilavottord-web/i18n'
-import { RecyclingCompanyImage, RecyclingCompanyForm } from '../components'
-import { SkilavottordAllRecyclingPartnersQuery } from '../RecyclingCompanies'
+import NavigationLinks from '@island.is/skilavottord-web/components/NavigationLinks/NavigationLinks'
+import PageHeader from '@island.is/skilavottord-web/components/PageHeader/PageHeader'
+import { RecyclingCompanyForm } from '../components'
+import { SkilavottordRecyclingPartnersQuery } from '../RecyclingCompanies'
const SkilavottordRecyclingPartnerQuery = gql`
query SkilavottordRecyclingPartnerQuery($input: RecyclingPartnerInput!) {
@@ -39,6 +40,7 @@ const SkilavottordRecyclingPartnerQuery = gql`
website
phone
active
+ municipalityId
}
}
`
@@ -58,14 +60,36 @@ const UpdateSkilavottordRecyclingPartnerMutation = gql`
website
phone
active
+ municipalityId
}
}
`
const RecyclingCompanyUpdate: FC> = () => {
+ const {
+ control,
+ reset,
+ handleSubmit,
+ setValue,
+ formState: { errors },
+ } = useForm({
+ mode: 'onChange',
+ })
+ const {
+ t: { recyclingCompanies: t, municipalities: mt, routes },
+ } = useI18n()
const { user } = useContext(UserContext)
const router = useRouter()
const { id } = router.query
+
+ const isMunicipalityPage = router.route === routes.municipalities.edit
+
+ // Show only recycling companies for the municipality
+ let partnerId = null
+ if (hasMunicipalityRole(user?.role)) {
+ partnerId = user?.partnerId
+ }
+
const { data, error, loading } = useQuery(
SkilavottordRecyclingPartnerQuery,
{
@@ -86,23 +110,34 @@ const RecyclingCompanyUpdate: FC> = () => {
variables: { input: { companyId: id } },
},
{
- query: SkilavottordAllRecyclingPartnersQuery,
+ query: SkilavottordRecyclingPartnersQuery,
+ variables: {
+ isMunicipalityPage: isMunicipalityPage,
+ municipalityId: partnerId,
+ },
},
],
},
)
- const {
- control,
- reset,
- handleSubmit,
- formState: { errors },
- } = useForm({
- mode: 'onChange',
- })
- const {
- t: { recyclingCompanies: t, recyclingFundSidenav: sidenavText, routes },
- } = useI18n()
+ let breadcrumbTitle = t.title
+ let title = t.recyclingCompany.view.title
+ let info = t.recyclingCompany.view.info
+ let activeSection = 2
+ let route = routes.recyclingCompanies.baseRoute
+
+ useEffect(() => {
+ setValue('isMunicipality', isMunicipalityPage)
+ }, [isMunicipalityPage, setValue])
+
+ // If coming from municipality page
+ if (isMunicipalityPage) {
+ activeSection = 1
+ breadcrumbTitle = mt.title
+ title = mt.municipality.view.title
+ info = mt.municipality.view.info
+ route = routes.municipalities.baseRoute
+ }
if (!user) {
return null
@@ -114,59 +149,46 @@ const RecyclingCompanyUpdate: FC> = () => {
return
}
+ const navigateToBaseRoute = () => {
+ const route = isMunicipalityPage
+ ? routes.municipalities.baseRoute
+ : routes.recyclingCompanies.baseRoute
+ return router.push(route)
+ }
+
const handleUpdateRecyclingPartner = handleSubmit(async (input) => {
// Not needed to be sent to the backend, causes error if it is sent
delete input.__typename
+ const municipalityId = input.municipalityId
+ input.municipalityId =
+ typeof municipalityId === 'object' && municipalityId?.value
+ ? municipalityId.value
+ : (municipalityId as string) || ''
+
const { errors } = await updateSkilavottordRecyclingPartner({
variables: { input },
})
if (!errors) {
- router.push(routes.recyclingCompanies.baseRoute).then(() => {
+ navigateToBaseRoute().then(() => {
toast.success(t.recyclingCompany.view.updated)
})
}
})
- const handleCancel = () => router.push(routes.recyclingCompanies.baseRoute)
+ const handleCancel = () => {
+ navigateToBaseRoute()
+ }
return (
- ['sections'][0],
- ].filter(Boolean)}
- activeSection={1}
- />
- }
- >
+ }>
> = () => {
)
}}
/>
-
-
-
-
- {t.recyclingCompany.view.title}
-
- {t.recyclingCompany.view.info}
-
-
-
-
-
-
-
-
+
{loading ? (
@@ -216,6 +216,7 @@ const RecyclingCompanyUpdate: FC> = () => {
control={control}
errors={errors}
editView
+ isMunicipalityPage={isMunicipalityPage}
/>
)}
diff --git a/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyForm/RecyclingCompanyForm.tsx b/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyForm/RecyclingCompanyForm.tsx
index 2b3680f5d5a3..b92ce4b20682 100644
--- a/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyForm/RecyclingCompanyForm.tsx
+++ b/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyForm/RecyclingCompanyForm.tsx
@@ -1,9 +1,10 @@
-import React, { BaseSyntheticEvent, FC } from 'react'
+import * as kennitala from 'kennitala'
+import React, { BaseSyntheticEvent, FC, useContext } from 'react'
import { Control, Controller, FieldError } from 'react-hook-form'
import { FieldValues } from 'react-hook-form/dist/types'
import { DeepMap } from 'react-hook-form/dist/types/utils'
-import * as kennitala from 'kennitala'
+import { gql, useQuery } from '@apollo/client'
import {
Box,
Button,
@@ -11,10 +12,17 @@ import {
GridColumn,
GridContainer,
GridRow,
+ Select,
Stack,
} from '@island.is/island-ui/core'
-import { useI18n } from '@island.is/skilavottord-web/i18n'
import { InputController } from '@island.is/shared/form-fields'
+import {
+ hasDeveloperRole,
+ hasMunicipalityRole,
+} from '@island.is/skilavottord-web/auth/utils'
+import UserContext from '@island.is/skilavottord-web/context/UserContext'
+import { Query } from '@island.is/skilavottord-web/graphql/schema'
+import { useI18n } from '@island.is/skilavottord-web/i18n'
interface RecyclingCompanyForm {
onSubmit: (
@@ -24,15 +32,50 @@ interface RecyclingCompanyForm {
errors: DeepMap
control: Control
editView?: boolean
+ isMunicipalityPage?: boolean | undefined
}
+export const SkilavottordAllMunicipalitiesQuery = gql`
+ query skilavottordAllMunicipalitiesQuery {
+ skilavottordAllMunicipalities {
+ companyId
+ companyName
+ }
+ }
+`
+
const RecyclingCompanyForm: FC<
React.PropsWithChildren
-> = ({ onSubmit, onCancel, control, errors, editView = false }) => {
+> = ({
+ onSubmit,
+ onCancel,
+ control,
+ errors,
+ editView = false,
+ isMunicipalityPage = false,
+}) => {
+ const { user } = useContext(UserContext)
+
const {
t: { recyclingCompanies: t },
} = useI18n()
+ const { data, error, loading } =
+ useQuery(SkilavottordAllMunicipalitiesQuery, {
+ fetchPolicy: 'cache-and-network',
+ }) || []
+
+ if (error) {
+ console.error('Failed to fetch municipalities:', error)
+ }
+
+ const municipalities = data?.skilavottordAllMunicipalities
+ .map((municipality) => ({
+ label: municipality.companyName,
+ value: municipality.companyId,
+ }))
+ .sort((a, b) => a.label.localeCompare(b.label))
+
return (
diff --git a/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyImage/RecyclingCompanyImage.tsx b/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyImage/RecyclingCompanyImage.tsx
deleted file mode 100644
index b535ec6907c6..000000000000
--- a/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyImage/RecyclingCompanyImage.tsx
+++ /dev/null
@@ -1,591 +0,0 @@
-import React from 'react'
-
-export default () => (
-
-)
diff --git a/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyImage/index.tsx b/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyImage/index.tsx
deleted file mode 100644
index dd5bae5ecc50..000000000000
--- a/apps/skilavottord/web/screens/RecyclingCompanies/components/RecyclingCompanyImage/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default as RecyclingCompanyImage } from './RecyclingCompanyImage'
diff --git a/apps/skilavottord/web/screens/RecyclingCompanies/components/index.ts b/apps/skilavottord/web/screens/RecyclingCompanies/components/index.ts
index 03d8d876f7f0..3c6b797ce257 100644
--- a/apps/skilavottord/web/screens/RecyclingCompanies/components/index.ts
+++ b/apps/skilavottord/web/screens/RecyclingCompanies/components/index.ts
@@ -1,2 +1 @@
-export { RecyclingCompanyImage } from './RecyclingCompanyImage'
export { RecyclingCompanyForm } from './RecyclingCompanyForm'
diff --git a/apps/skilavottord/web/screens/RecyclingFund/Overview/Overview.tsx b/apps/skilavottord/web/screens/RecyclingFund/Overview/Overview.tsx
index ac39db0f53fa..c2aa81b5f565 100644
--- a/apps/skilavottord/web/screens/RecyclingFund/Overview/Overview.tsx
+++ b/apps/skilavottord/web/screens/RecyclingFund/Overview/Overview.tsx
@@ -1,29 +1,29 @@
-import React, { FC, useContext, useRef, useEffect } from 'react'
-import gql from 'graphql-tag'
import { useQuery } from '@apollo/client'
+import gql from 'graphql-tag'
import NextLink from 'next/link'
+import React, { FC, useContext, useEffect, useRef } from 'react'
-import { useI18n } from '@island.is/skilavottord-web/i18n'
import {
Box,
Breadcrumbs,
- GridColumn,
- GridRow,
LoadingDots,
Stack,
Text,
} from '@island.is/island-ui/core'
+import { hasPermission } from '@island.is/skilavottord-web/auth/utils'
+import { NotFound } from '@island.is/skilavottord-web/components'
import { PartnerPageLayout } from '@island.is/skilavottord-web/components/Layouts'
-import { Sidenav, NotFound } from '@island.is/skilavottord-web/components'
import { UserContext } from '@island.is/skilavottord-web/context'
-import { hasPermission } from '@island.is/skilavottord-web/auth/utils'
import {
Query,
Role,
Vehicle,
} from '@island.is/skilavottord-web/graphql/schema'
+import { useI18n } from '@island.is/skilavottord-web/i18n'
-import { CarsTable, RecyclingCompanyImage } from './components'
+import NavigationLinks from '@island.is/skilavottord-web/components/NavigationLinks/NavigationLinks'
+import PageHeader from '@island.is/skilavottord-web/components/PageHeader/PageHeader'
+import { CarsTable } from './components'
export const SkilavottordVehiclesQuery = gql`
query skilavottordVehiclesQuery($after: String!) {
@@ -40,8 +40,17 @@ export const SkilavottordVehiclesQuery = gql`
createdAt
recyclingRequests {
id
+ recyclingPartnerId
nameOfRequestor
createdAt
+ recyclingPartner {
+ companyId
+ companyName
+ municipalityId
+ municipality {
+ companyName
+ }
+ }
}
}
}
@@ -51,7 +60,7 @@ export const SkilavottordVehiclesQuery = gql`
const Overview: FC> = () => {
const { user } = useContext(UserContext)
const {
- t: { recyclingFundOverview: t, recyclingFundSidenav: sidenavText, routes },
+ t: { recyclingFundOverview: t, routes },
} = useI18n()
const { data, loading, fetchMore } = useQuery(
SkilavottordVehiclesQuery,
@@ -112,35 +121,7 @@ const Overview: FC> = () => {
}
return (
- ['sections'][0],
- ].filter(Boolean)}
- activeSection={0}
- />
- }
- >
+ }>
> = () => {
)
}}
/>
-
-
-
-
- {t.title}
-
- {t.info}
-
-
-
-
-
-
-
-
+
+
+
{t.subtitles.deregistered}
{vehicles && vehicles.length > 0 ? (
diff --git a/apps/skilavottord/web/screens/RecyclingFund/Overview/components/CarsTable/CarsTable.tsx b/apps/skilavottord/web/screens/RecyclingFund/Overview/components/CarsTable/CarsTable.tsx
index 9b1af3053bd2..d7d449f7d5f7 100644
--- a/apps/skilavottord/web/screens/RecyclingFund/Overview/components/CarsTable/CarsTable.tsx
+++ b/apps/skilavottord/web/screens/RecyclingFund/Overview/components/CarsTable/CarsTable.tsx
@@ -1,7 +1,7 @@
-import React, { FC } from 'react'
-import { Stack, Table as T, Text } from '@island.is/island-ui/core'
-import { getDate, getYear } from '@island.is/skilavottord-web/utils'
+import { Table as T, Text } from '@island.is/island-ui/core'
import { Vehicle } from '@island.is/skilavottord-web/graphql/schema'
+import { getDate, getYear } from '@island.is/skilavottord-web/utils'
+import React, { FC } from 'react'
interface TableProps {
titles: string[]
@@ -27,21 +27,27 @@ export const CarsTable: FC> = ({
{vehicles.map(
({ vehicleId, vehicleType, newregDate, recyclingRequests }) => {
- return recyclingRequests?.map(({ createdAt, nameOfRequestor }) => {
- const modelYear = getYear(newregDate)
- const deregistrationDate = getDate(createdAt)
- return (
-
-
- {vehicleId}
-
- {vehicleType}
- {modelYear}
- {nameOfRequestor}
- {deregistrationDate}
-
- )
- })
+ return recyclingRequests?.map(
+ ({ createdAt, nameOfRequestor, recyclingPartner }, index) => {
+ const modelYear = getYear(newregDate)
+ const deregistrationDate = getDate(createdAt)
+ return (
+
+
+ {vehicleId}
+
+ {vehicleType}
+ {modelYear ?? ''}
+ {nameOfRequestor}
+
+ {recyclingPartner?.municipality?.companyName ??
+ nameOfRequestor}
+
+ {deregistrationDate ?? ''}
+
+ )
+ },
+ )
},
)}
diff --git a/apps/skilavottord/web/screens/RecyclingFund/Overview/components/RecyclingCompanyImage/RecyclingCompanyImage.tsx b/apps/skilavottord/web/screens/RecyclingFund/Overview/components/RecyclingCompanyImage/RecyclingCompanyImage.tsx
deleted file mode 100644
index b535ec6907c6..000000000000
--- a/apps/skilavottord/web/screens/RecyclingFund/Overview/components/RecyclingCompanyImage/RecyclingCompanyImage.tsx
+++ /dev/null
@@ -1,591 +0,0 @@
-import React from 'react'
-
-export default () => (
-
-)
diff --git a/apps/skilavottord/web/screens/RecyclingFund/Overview/components/RecyclingCompanyImage/index.tsx b/apps/skilavottord/web/screens/RecyclingFund/Overview/components/RecyclingCompanyImage/index.tsx
deleted file mode 100644
index dd5bae5ecc50..000000000000
--- a/apps/skilavottord/web/screens/RecyclingFund/Overview/components/RecyclingCompanyImage/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default as RecyclingCompanyImage } from './RecyclingCompanyImage'
diff --git a/apps/skilavottord/web/screens/RecyclingFund/Overview/components/index.ts b/apps/skilavottord/web/screens/RecyclingFund/Overview/components/index.ts
index 052f49f9b3bb..b34076c09fb2 100644
--- a/apps/skilavottord/web/screens/RecyclingFund/Overview/components/index.ts
+++ b/apps/skilavottord/web/screens/RecyclingFund/Overview/components/index.ts
@@ -1,2 +1 @@
-export { RecyclingCompanyImage } from './RecyclingCompanyImage'
export { CarsTable } from './CarsTable'
diff --git a/apps/skilavottord/web/utils/accessUtils.ts b/apps/skilavottord/web/utils/accessUtils.ts
new file mode 100644
index 000000000000..3cf3c09c4300
--- /dev/null
+++ b/apps/skilavottord/web/utils/accessUtils.ts
@@ -0,0 +1,17 @@
+import { hasMunicipalityRole } from '../auth/utils'
+import { Role, SkilavottordUser } from '../graphql/schema'
+
+export const getPartnerId = (
+ user: SkilavottordUser | undefined,
+ municipalityId: string | null,
+ partnerId: string | null,
+ role: Role,
+) => {
+ // If the user has municipality role, then he can only create a new access under the same municipality
+ if (hasMunicipalityRole(user?.role) && hasMunicipalityRole(role)) {
+ return user.partnerId
+ }
+
+ // If selected role is municipality, use municipalityId, else use partnerId
+ return hasMunicipalityRole(role) ? municipalityId ?? null : partnerId ?? null
+}
diff --git a/apps/skilavottord/web/utils/index.ts b/apps/skilavottord/web/utils/index.ts
index b29210094cef..c4cbce411716 100644
--- a/apps/skilavottord/web/utils/index.ts
+++ b/apps/skilavottord/web/utils/index.ts
@@ -2,3 +2,4 @@ export { default as isBrowser } from './isBrowser'
export * from './filterUtils'
export * from './dateUtils'
export * from './roleUtils'
+export * from './accessUtils'
diff --git a/apps/skilavottord/web/utils/roleUtils.ts b/apps/skilavottord/web/utils/roleUtils.ts
index 793c56126f3f..d1d13437c376 100644
--- a/apps/skilavottord/web/utils/roleUtils.ts
+++ b/apps/skilavottord/web/utils/roleUtils.ts
@@ -7,14 +7,16 @@ export const getRoleTranslation = (role: Role, locale: Locale): string => {
switch (role) {
case Role.developer:
return locale === 'is' ? 'Forritari' : 'Developer'
- case 'recyclingCompany':
+ case Role.recyclingCompany:
return locale === 'is' ? 'Móttökuaðili' : 'Recycling Company'
- case 'recyclingFund':
+ case Role.recyclingFund:
return locale === 'is' ? 'Úrvinnslusjóður' : 'Recycling Fund'
- case 'recyclingCompanyAdmin':
+ case Role.recyclingCompanyAdmin:
return locale === 'is'
? 'Móttökuaðili umsýsla'
: 'Recycling Company Admin'
+ case Role.municipality:
+ return locale === 'is' ? 'Sveitarfélag' : 'Municipality'
default:
return startCase(role)
}
diff --git a/apps/skilavottord/ws/migrations/202412010017109-addcolumns-recycling-partners-municipality.js b/apps/skilavottord/ws/migrations/202412010017109-addcolumns-recycling-partners-municipality.js
new file mode 100644
index 000000000000..a4c39db45c3c
--- /dev/null
+++ b/apps/skilavottord/ws/migrations/202412010017109-addcolumns-recycling-partners-municipality.js
@@ -0,0 +1,27 @@
+const { DataTypes } = require('sequelize')
+
+module.exports = {
+ up: async (queryInterface) => {
+ await queryInterface.addColumn('recycling_partner', 'is_municipality', {
+ type: DataTypes.BOOLEAN,
+ allowNull: false,
+ defaultValue: false,
+ })
+ await queryInterface.addColumn('recycling_partner', 'municipality_id', {
+ type: DataTypes.STRING(50),
+ allowNull: true,
+ })
+ await queryInterface.addIndex('recycling_partner', ['is_municipality'], {
+ name: 'idx_recycling_partner_is_municipality',
+ })
+ },
+
+ down: async (queryInterface) => {
+ /* await queryInterface.removeIndex(
+ 'recycling_partner',
+ 'idx_recycling_partner_is_municipality',
+ )
+ await queryInterface.removeColumn('recycling_partner', 'is_municipality')
+ await queryInterface.removeColumn('recycling_partner', 'municipality_id')*/
+ },
+}
diff --git a/apps/skilavottord/ws/migrations/202412230017109-add-view-municipality.js b/apps/skilavottord/ws/migrations/202412230017109-add-view-municipality.js
new file mode 100644
index 000000000000..58ec3c7d0d83
--- /dev/null
+++ b/apps/skilavottord/ws/migrations/202412230017109-add-view-municipality.js
@@ -0,0 +1,16 @@
+module.exports = {
+ up: async (queryInterface) => {
+ await queryInterface.sequelize.query(`
+ CREATE VIEW municipality_view AS
+ SELECT company_id AS "id", company_name AS "company_name", address, postnumber, city, email, phone, website, active
+ FROM recycling_partner
+ WHERE is_municipality = true;
+ `)
+ },
+
+ down: async (queryInterface) => {
+ await queryInterface.sequelize.query(`
+ DROP VIEW IF EXISTS municipality_view
+ `)
+ },
+}
diff --git a/apps/skilavottord/ws/seeders/20201010211010-seed-recycling-request.js b/apps/skilavottord/ws/seeders/20201010211010-seed-recycling-request.js
index 41dff915e2ba..accdf1fd9e0c 100644
--- a/apps/skilavottord/ws/seeders/20201010211010-seed-recycling-request.js
+++ b/apps/skilavottord/ws/seeders/20201010211010-seed-recycling-request.js
@@ -7,7 +7,7 @@ module.exports = {
[
{
id: 'a1fd62db-18a6-4741-88eb-a7b7a7e05833',
- vehicle_id: 'ftm-522',
+ vehicle_id: 'VM006',
request_type: 'xxxxxxx',
name_of_requestor: 'xxxxxxx',
recycling_partner_id: '8888888888',
@@ -16,7 +16,7 @@ module.exports = {
},
{
id: 'b1fd62db-18a6-4741-88eb-a7b7a7e05833',
- vehicle_id: 'mhs-583',
+ vehicle_id: 'LT579',
request_type: 'xxxxxxx',
name_of_requestor: 'xxxxxxx',
recycling_partner_id: '9999999999',
@@ -25,7 +25,7 @@ module.exports = {
},
{
id: 'c1fd62db-18a6-4741-88eb-a7b7a7e05833',
- vehicle_id: 'aes-135',
+ vehicle_id: 'AE135',
request_type: 'xxxxxxx',
name_of_requestor: 'xxxxxxx',
recycling_partner_id: null,
@@ -43,7 +43,7 @@ module.exports = {
},
{
id: 'a3fd62db-18a6-4741-88eb-a7b7a7e05833',
- vehicle_id: 'aes-135',
+ vehicle_id: 'AE135',
request_type: 'pendingRecycle',
recycling_partner_id: null,
name_of_requestor: 'xxxxxxx',
@@ -52,7 +52,7 @@ module.exports = {
},
{
id: 'a4fd62db-18a6-4741-88eb-a7b7a7e05833',
- vehicle_id: 'aes-135',
+ vehicle_id: 'AE135',
request_type: 'handOver',
name_of_requestor: 'xxxxxxx',
recycling_partner_id: null,
@@ -61,7 +61,7 @@ module.exports = {
},
{
id: 'a5fd62db-18a6-4741-88eb-a7b7a7e05833',
- vehicle_id: 'ftm-522',
+ vehicle_id: 'FT522',
request_type: 'pendingRecycle',
name_of_requestor: 'xxxxxxx',
recycling_partner_id: null,
diff --git a/apps/skilavottord/ws/src/app/app.module.ts b/apps/skilavottord/ws/src/app/app.module.ts
index 7f8d55a37ef5..0b0a3eab281d 100644
--- a/apps/skilavottord/ws/src/app/app.module.ts
+++ b/apps/skilavottord/ws/src/app/app.module.ts
@@ -1,24 +1,25 @@
-import { Module } from '@nestjs/common'
import { ApolloDriver } from '@nestjs/apollo'
+import { Module } from '@nestjs/common'
import { GraphQLModule } from '@nestjs/graphql'
import { SequelizeModule } from '@nestjs/sequelize'
import { BASE_PATH } from '@island.is/skilavottord/consts'
+import { AuthModule as AuthJwtModule } from '@island.is/auth-nest-tools'
+import { environment } from '../environments'
import {
AccessControlModule,
AuthModule,
+ FjarsyslaModule,
GdprModule,
- VehicleModule,
- RecyclingRequestModule,
+ MunicipalityModule,
RecyclingPartnerModule,
- VehicleOwnerModule,
+ RecyclingRequestModule,
SamgongustofaModule,
- FjarsyslaModule,
+ VehicleModule,
+ VehicleOwnerModule,
} from './modules'
-import { AuthModule as AuthJwtModule } from '@island.is/auth-nest-tools'
import { SequelizeConfigService } from './sequelizeConfig.service'
-import { environment } from '../environments'
const debug = process.env.NODE_ENV === 'development'
const playground = debug || process.env.GQL_PLAYGROUND_ENABLED === 'true'
@@ -51,6 +52,7 @@ const autoSchemaFile = environment.production
RecyclingPartnerModule,
VehicleModule,
VehicleOwnerModule,
+ MunicipalityModule,
],
})
export class AppModule {}
diff --git a/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.model.ts b/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.model.ts
index 578be8d18883..5a32f8913fed 100644
--- a/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.model.ts
+++ b/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.model.ts
@@ -63,6 +63,7 @@ export class AccessControlModel extends Model {
})
recyclingLocation?: string
+ @Field({ nullable: true })
@ForeignKey(() => RecyclingPartnerModel)
@Column({
type: DataType.STRING,
diff --git a/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.resolver.ts b/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.resolver.ts
index 61dffa84992a..8eb1b2081e75 100644
--- a/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.resolver.ts
+++ b/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.resolver.ts
@@ -17,7 +17,12 @@ import { AccessControlModel } from './accessControl.model'
import { AccessControlService } from './accessControl.service'
@Authorize({
- roles: [Role.developer, Role.recyclingFund, Role.recyclingCompanyAdmin],
+ roles: [
+ Role.developer,
+ Role.recyclingFund,
+ Role.recyclingCompanyAdmin,
+ Role.municipality,
+ ],
})
@Resolver(() => AccessControlModel)
export class AccessControlResolver {
@@ -60,6 +65,18 @@ export class AccessControlResolver {
@CurrentUser() user: User,
): Promise {
const isDeveloper = user.role === Role.developer
+
+ if (user.role === Role.municipality) {
+ try {
+ return this.accessControlService.findByRecyclingPartner(user.partnerId)
+ } catch (error) {
+ throw new ApolloError(
+ 'Failed to fetch municipality access controls',
+ 'MUNICIPALITY_ACCESS_ERROR',
+ )
+ }
+ }
+
return this.accessControlService.findAll(isDeveloper)
}
diff --git a/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.service.ts b/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.service.ts
index 052c15f25b88..e9d68cb09c84 100644
--- a/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.service.ts
+++ b/apps/skilavottord/ws/src/app/modules/accessControl/accessControl.service.ts
@@ -1,15 +1,15 @@
-import { InjectModel } from '@nestjs/sequelize'
import { Injectable } from '@nestjs/common'
+import { InjectModel } from '@nestjs/sequelize'
import { Op } from 'sequelize'
import { RecyclingPartnerModel } from '../recyclingPartner'
-import { AccessControlModel, AccessControlRole } from './accessControl.model'
import {
CreateAccessControlInput,
DeleteAccessControlInput,
UpdateAccessControlInput,
} from './accessControl.input'
+import { AccessControlModel, AccessControlRole } from './accessControl.model'
@Injectable()
export class AccessControlService {
@@ -34,13 +34,26 @@ export class AccessControlService {
async findByRecyclingPartner(
partnerId: string,
): Promise {
+ let partnerIds = []
+
+ // Get all sub recycling partners of the municipality
+ // else get all
+ if (partnerId) {
+ const subRecyclingPartners = await RecyclingPartnerModel.findAll({
+ where: { municipalityId: partnerId },
+ })
+
+ partnerIds = [
+ partnerId,
+ ...subRecyclingPartners.map((partner) => partner.companyId),
+ ]
+ } else {
+ partnerIds = null
+ }
+
return this.accessControlModel.findAll({
- where: { partnerId, role: ['recyclingCompany', 'recyclingCompanyAdmin'] },
- include: [
- {
- model: RecyclingPartnerModel,
- },
- ],
+ where: { partnerId: partnerIds },
+ include: [RecyclingPartnerModel],
})
}
diff --git a/apps/skilavottord/ws/src/app/modules/auth/auth.guard.ts b/apps/skilavottord/ws/src/app/modules/auth/auth.guard.ts
index 1c667edf14e4..2ab026916e5b 100644
--- a/apps/skilavottord/ws/src/app/modules/auth/auth.guard.ts
+++ b/apps/skilavottord/ws/src/app/modules/auth/auth.guard.ts
@@ -75,27 +75,8 @@ export class AuthGuard implements CanActivate {
export const Authorize = (
{ roles = [] }: AuthorizeOptions = { roles: [] },
): MethodDecorator & ClassDecorator => {
- logger.info(`car-recycling: AuthGuard environment #1`, {
- environment: process.env.NODE_ENV,
- isProduction: isRunningOnEnvironment('production'),
- })
-
- // IdsUserGuard is causing constant reload on local and DEV in the skilavottord-web
- // To 'fix' it for now we just skip using it for non production
- if (
- process.env.NODE_ENV !== 'production' ||
- !isRunningOnEnvironment('production')
- ) {
- logger.info('`car-recycling: AuthGuard - skipping IdsUserGuard')
- return applyDecorators(
- SetMetadata('roles', roles),
- UseGuards(AuthGuard, RolesGuard),
- )
- }
-
- // We are getting Invalid User error on PROD if we skip IdsUserGuard for some reason
return applyDecorators(
SetMetadata('roles', roles),
- UseGuards(IdsUserGuard, AuthGuard, RolesGuard),
+ UseGuards(AuthGuard, RolesGuard),
)
}
diff --git a/apps/skilavottord/ws/src/app/modules/auth/user.model.ts b/apps/skilavottord/ws/src/app/modules/auth/user.model.ts
index 3a650dad9418..24ee03634b72 100644
--- a/apps/skilavottord/ws/src/app/modules/auth/user.model.ts
+++ b/apps/skilavottord/ws/src/app/modules/auth/user.model.ts
@@ -6,6 +6,7 @@ export enum Role {
recyclingCompanyAdmin = 'recyclingCompanyAdmin',
recyclingFund = 'recyclingFund',
citizen = 'citizen',
+ municipality = 'municipality',
}
registerEnumType(Role, { name: 'Role' })
diff --git a/apps/skilavottord/ws/src/app/modules/index.ts b/apps/skilavottord/ws/src/app/modules/index.ts
index e4b1468e21b0..8becbef0fe93 100644
--- a/apps/skilavottord/ws/src/app/modules/index.ts
+++ b/apps/skilavottord/ws/src/app/modules/index.ts
@@ -7,3 +7,4 @@ export { RecyclingRequestModule } from './recyclingRequest/recyclingRequest.modu
export { SamgongustofaModule } from './samgongustofa/samgongustofa.module'
export { VehicleModule } from './vehicle/vehicle.module'
export { VehicleOwnerModule } from './vehicleOwner/vehicleOwner.module'
+export { MunicipalityModule } from './municipality/municipality.module'
diff --git a/apps/skilavottord/ws/src/app/modules/municipality/index.ts b/apps/skilavottord/ws/src/app/modules/municipality/index.ts
new file mode 100644
index 000000000000..fe55d7539d2c
--- /dev/null
+++ b/apps/skilavottord/ws/src/app/modules/municipality/index.ts
@@ -0,0 +1,2 @@
+export { MunicipalityModel } from './municipality.model'
+export { MunicipalityService } from './municipality.service'
diff --git a/apps/skilavottord/ws/src/app/modules/municipality/municipality.model.ts b/apps/skilavottord/ws/src/app/modules/municipality/municipality.model.ts
new file mode 100644
index 000000000000..1f969762d852
--- /dev/null
+++ b/apps/skilavottord/ws/src/app/modules/municipality/municipality.model.ts
@@ -0,0 +1,20 @@
+import { Field, ID, ObjectType } from '@nestjs/graphql'
+import { Column, DataType, Model, Table } from 'sequelize-typescript'
+
+@ObjectType('Municipality')
+@Table({ tableName: 'municipality_view', timestamps: false })
+export class MunicipalityModel extends Model {
+ @Field(() => ID)
+ @Column({
+ type: DataType.STRING,
+ field: 'id',
+ })
+ companyId!: string
+
+ @Field()
+ @Column({
+ type: DataType.STRING,
+ field: 'company_name',
+ })
+ companyName!: string
+}
diff --git a/apps/skilavottord/ws/src/app/modules/municipality/municipality.module.ts b/apps/skilavottord/ws/src/app/modules/municipality/municipality.module.ts
new file mode 100644
index 000000000000..bf7f93719f39
--- /dev/null
+++ b/apps/skilavottord/ws/src/app/modules/municipality/municipality.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common'
+import { SequelizeModule } from '@nestjs/sequelize'
+
+import { MunicipalityModel } from './municipality.model'
+import { MunicipalityResolver } from './municipality.resolver'
+import { MunicipalityService } from './municipality.service'
+
+@Module({
+ imports: [SequelizeModule.forFeature([MunicipalityModel])],
+ providers: [MunicipalityResolver, MunicipalityService],
+ exports: [MunicipalityService],
+})
+export class MunicipalityModule {}
diff --git a/apps/skilavottord/ws/src/app/modules/municipality/municipality.resolver.ts b/apps/skilavottord/ws/src/app/modules/municipality/municipality.resolver.ts
new file mode 100644
index 000000000000..e33be348b5cc
--- /dev/null
+++ b/apps/skilavottord/ws/src/app/modules/municipality/municipality.resolver.ts
@@ -0,0 +1,22 @@
+import { Query, Resolver } from '@nestjs/graphql'
+
+import { Authorize, Role } from '../auth'
+
+import { MunicipalityModel } from './municipality.model'
+import { MunicipalityService } from './municipality.service'
+
+@Authorize()
+@Resolver(() => MunicipalityModel)
+export class MunicipalityResolver {
+ constructor(private municipalityService: MunicipalityService) {}
+
+ @Authorize({
+ roles: [Role.developer, Role.recyclingFund, Role.municipality],
+ })
+ @Query(() => [MunicipalityModel], {
+ name: 'skilavottordAllMunicipalities',
+ })
+ async skilavottordAllMunicipalities(): Promise {
+ return this.municipalityService.findAll()
+ }
+}
diff --git a/apps/skilavottord/ws/src/app/modules/municipality/municipality.service.ts b/apps/skilavottord/ws/src/app/modules/municipality/municipality.service.ts
new file mode 100644
index 000000000000..35e3231a635d
--- /dev/null
+++ b/apps/skilavottord/ws/src/app/modules/municipality/municipality.service.ts
@@ -0,0 +1,16 @@
+import { Injectable } from '@nestjs/common'
+import { InjectModel } from '@nestjs/sequelize'
+
+import { MunicipalityModel } from './municipality.model'
+
+@Injectable()
+export class MunicipalityService {
+ constructor(
+ @InjectModel(MunicipalityModel)
+ private municipalityModel: typeof MunicipalityModel,
+ ) {}
+
+ async findAll(): Promise {
+ return await this.municipalityModel.findAll()
+ }
+}
diff --git a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.input.ts b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.input.ts
index 5aeee322ba5d..645cbbfe50a4 100644
--- a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.input.ts
+++ b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.input.ts
@@ -37,6 +37,12 @@ export class CreateRecyclingPartnerInput {
@Field()
email?: string
+
+ @Field()
+ isMunicipality?: boolean
+
+ @Field({ nullable: true })
+ municipalityId?: string
}
@InputType()
@@ -70,4 +76,10 @@ export class UpdateRecyclingPartnerInput {
@Field()
email?: string
+
+ @Field({ nullable: true })
+ isMunicipality?: boolean
+
+ @Field({ nullable: true })
+ municipalityId?: string
}
diff --git a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.model.ts b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.model.ts
index 40133e5c22dd..c36c7e644ded 100644
--- a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.model.ts
+++ b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.model.ts
@@ -1,16 +1,17 @@
import { Field, ID, ObjectType } from '@nestjs/graphql'
import {
+ BelongsTo,
Column,
+ CreatedAt,
DataType,
Model,
+ PrimaryKey,
Table,
- CreatedAt,
UpdatedAt,
- HasMany,
- PrimaryKey,
} from 'sequelize-typescript'
import { RecyclingRequestModel } from '../recyclingRequest'
+import { MunicipalityModel } from '../municipality/municipality.model'
@ObjectType('RecyclingPartner')
@Table({ tableName: 'recycling_partner' })
@@ -98,7 +99,27 @@ export class RecyclingPartnerModel extends Model {
})
updatedAt: Date
+ @Field()
+ @Column({
+ type: DataType.BOOLEAN,
+ field: 'is_municipality',
+ })
+ isMunicipality!: boolean
+
+ @Field({ nullable: true }) // Ensure this field is nullable in GraphQL
+ @Column({
+ type: DataType.STRING,
+ field: 'municipality_id',
+ allowNull: true, // Ensure this field allows null in Sequelize
+ })
+ municipalityId?: string
+
@Field(() => [RecyclingRequestModel])
- @HasMany(() => RecyclingRequestModel)
recyclingRequests?: typeof RecyclingRequestModel[]
+ @Field(() => MunicipalityModel, { nullable: true })
+ @BelongsTo(() => MunicipalityModel, {
+ foreignKey: 'municipality_id',
+ as: 'municipality',
+ })
+ municipality?: MunicipalityModel
}
diff --git a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.module.ts b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.module.ts
index 64ee9ee75ae0..05e7a167af7e 100644
--- a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.module.ts
+++ b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.module.ts
@@ -4,9 +4,13 @@ import { SequelizeModule } from '@nestjs/sequelize'
import { RecyclingPartnerModel } from './recyclingPartner.model'
import { RecyclingPartnerResolver } from './recyclingPartner.resolver'
import { RecyclingPartnerService } from './recyclingPartner.service'
+import { MunicipalityModel } from '../municipality/municipality.model'
@Module({
- imports: [SequelizeModule.forFeature([RecyclingPartnerModel])],
+ imports: [
+ SequelizeModule.forFeature([RecyclingPartnerModel]),
+ SequelizeModule.forFeature([MunicipalityModel]),
+ ],
providers: [RecyclingPartnerResolver, RecyclingPartnerService],
exports: [RecyclingPartnerService],
})
diff --git a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.resolver.ts b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.resolver.ts
index 66fb91a354a4..e78186fc61df 100644
--- a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.resolver.ts
+++ b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.resolver.ts
@@ -1,15 +1,20 @@
+import {
+ BadRequestException,
+ ConflictException,
+ InternalServerErrorException,
+ NotFoundException,
+} from '@nestjs/common'
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'
-import { ConflictException, NotFoundException } from '@nestjs/common'
import { Authorize, Role } from '../auth'
-import { RecyclingPartnerModel } from './recyclingPartner.model'
-import { RecyclingPartnerService } from './recyclingPartner.service'
import {
CreateRecyclingPartnerInput,
RecyclingPartnerInput,
UpdateRecyclingPartnerInput,
} from './recyclingPartner.input'
+import { RecyclingPartnerModel } from './recyclingPartner.model'
+import { RecyclingPartnerService } from './recyclingPartner.service'
@Authorize()
@Resolver(() => RecyclingPartnerModel)
@@ -17,13 +22,39 @@ export class RecyclingPartnerResolver {
constructor(private recyclingPartnerService: RecyclingPartnerService) {}
@Authorize({
- roles: [Role.developer, Role.recyclingFund],
+ roles: [Role.developer, Role.recyclingFund, Role.municipality],
})
- @Query(() => [RecyclingPartnerModel])
- async skilavottordAllRecyclingPartners(): Promise {
+ @Query(() => [RecyclingPartnerModel], {
+ name: 'skilavottordAllRecyclingPartners',
+ })
+ async getAllRecyclingPartners(): Promise {
return this.recyclingPartnerService.findAll()
}
+ @Authorize({
+ roles: [Role.developer, Role.recyclingFund, Role.municipality],
+ })
+ @Query(() => [RecyclingPartnerModel], {
+ name: 'skilavottordRecyclingPartners',
+ })
+ async skilavottordRecyclingPartners(
+ @Args('isMunicipalityPage', { type: () => Boolean, nullable: true })
+ isMunicipalityPage: boolean,
+ @Args('municipalityId', { type: () => String, nullable: true })
+ municipalityId: string | null,
+ ): Promise {
+ try {
+ return this.recyclingPartnerService.findRecyclingPartners(
+ isMunicipalityPage,
+ municipalityId,
+ )
+ } catch (error) {
+ throw new InternalServerErrorException(
+ `Failed to fetch recycling partners: ${error.message}`,
+ )
+ }
+ }
+
@Query(() => [RecyclingPartnerModel])
async skilavottordAllActiveRecyclingPartners(): Promise<
RecyclingPartnerModel[]
@@ -32,7 +63,7 @@ export class RecyclingPartnerResolver {
}
@Authorize({
- roles: [Role.developer, Role.recyclingFund],
+ roles: [Role.developer, Role.recyclingFund, Role.municipality],
})
@Query(() => RecyclingPartnerModel)
async skilavottordRecyclingPartner(
@@ -43,7 +74,7 @@ export class RecyclingPartnerResolver {
}
@Authorize({
- roles: [Role.developer, Role.recyclingFund],
+ roles: [Role.developer, Role.recyclingFund, Role.municipality],
})
@Mutation(() => RecyclingPartnerModel)
async createSkilavottordRecyclingPartner(
@@ -64,7 +95,7 @@ export class RecyclingPartnerResolver {
}
@Authorize({
- roles: [Role.developer, Role.recyclingFund],
+ roles: [Role.developer, Role.recyclingFund, Role.municipality],
})
@Mutation(() => RecyclingPartnerModel)
async updateSkilavottordRecyclingPartner(
diff --git a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.service.ts b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.service.ts
index d3fa410e95b3..1cf954bf3ec6 100644
--- a/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.service.ts
+++ b/apps/skilavottord/ws/src/app/modules/recyclingPartner/recyclingPartner.service.ts
@@ -6,6 +6,7 @@ import {
CreateRecyclingPartnerInput,
UpdateRecyclingPartnerInput,
} from './recyclingPartner.input'
+import { Op } from 'sequelize'
@Injectable()
export class RecyclingPartnerService {
@@ -24,6 +25,36 @@ export class RecyclingPartnerService {
})
}
+ async findRecyclingPartners(
+ isMunicipalityPage: boolean,
+ municipalityId: string | null | undefined,
+ ): Promise {
+ try {
+ // If Role.municipality, return all recycling partners that are not municipalities
+ if (municipalityId) {
+ return await this.recyclingPartnerModel.findAll({
+ where: {
+ isMunicipality: false,
+ municipalityId: municipalityId,
+ },
+ })
+ }
+
+ if (isMunicipalityPage) {
+ return await this.recyclingPartnerModel.findAll({
+ where: { isMunicipality: true },
+ })
+ }
+ return await this.recyclingPartnerModel.findAll({
+ where: {
+ [Op.or]: [{ isMunicipality: false }, { isMunicipality: null }],
+ },
+ })
+ } catch (error) {
+ throw new Error(`Failed to fetch recycling partners: ${error.message}`)
+ }
+ }
+
async findOne(companyId: string): Promise {
return this.recyclingPartnerModel.findOne({
where: { companyId },
@@ -50,4 +81,21 @@ export class RecyclingPartnerService {
)
return accessControl
}
+
+ /**
+ * Get the id of the municipality or recyclingpartner that is paying for the recycling request
+ * @param companyId
+ * @returns
+ */
+ async getPayingPartnerId(companyId: string): Promise {
+ const partner = await this.recyclingPartnerModel.findOne({
+ where: { companyId },
+ })
+
+ if (!partner) {
+ throw new Error(`Partner not found for company ID: ${companyId}`)
+ }
+
+ return partner.municipalityId ?? partner.companyId
+ }
}
diff --git a/apps/skilavottord/ws/src/app/modules/recyclingRequest/recyclingRequest.service.ts b/apps/skilavottord/ws/src/app/modules/recyclingRequest/recyclingRequest.service.ts
index afb5ea568a87..630c8b496f40 100644
--- a/apps/skilavottord/ws/src/app/modules/recyclingRequest/recyclingRequest.service.ts
+++ b/apps/skilavottord/ws/src/app/modules/recyclingRequest/recyclingRequest.service.ts
@@ -38,7 +38,7 @@ export class RecyclingRequestService {
private httpService: HttpService,
private fjarsyslaService: FjarsyslaService,
@Inject(forwardRef(() => RecyclingPartnerService))
- private recycllingPartnerService: RecyclingPartnerService,
+ private recyclingPartnerService: RecyclingPartnerService,
private vehicleService: VehicleService,
private icelandicTransportAuthorityServices: IcelandicTransportAuthorityServices,
) {}
@@ -48,6 +48,9 @@ export class RecyclingRequestService {
disposalStation: string,
vehicle: VehicleModel,
) {
+ const disposalStationId =
+ await this.recyclingPartnerService.getPayingPartnerId(disposalStation)
+
try {
const { restAuthUrl, restDeRegUrl, restUsername, restPassword } =
environment.samgongustofa
@@ -78,7 +81,7 @@ export class RecyclingRequestService {
const jsonDeRegBody = JSON.stringify({
permno: vehiclePermno,
deRegisterDate: format(new Date(), "yyyy-MM-dd'T'HH:mm:ss"),
- disposalStation: disposalStation,
+ disposalStation: disposalStationId,
explanation: 'Rafrænt afskráning',
mileage: vehicle.mileage ?? 0,
plateCount: vehicle.plateCount,
@@ -264,7 +267,7 @@ export class RecyclingRequestService {
// is partnerId null?
if (partnerId) {
newRecyclingRequest.recyclingPartnerId = partnerId
- const partner = await this.recycllingPartnerService.findOne(partnerId)
+ const partner = await this.recyclingPartnerService.findOne(partnerId)
if (!partner) {
this.logger.error(
`car-recycling: The recycling station is not in the recycling station list`,
diff --git a/apps/skilavottord/ws/src/app/modules/vehicle/vehicle.resolver.ts b/apps/skilavottord/ws/src/app/modules/vehicle/vehicle.resolver.ts
index d8a1516ffda1..ef376ec07be1 100644
--- a/apps/skilavottord/ws/src/app/modules/vehicle/vehicle.resolver.ts
+++ b/apps/skilavottord/ws/src/app/modules/vehicle/vehicle.resolver.ts
@@ -18,15 +18,17 @@ export class VehicleResolver {
private samgongustofaService: SamgongustofaService,
) {}
- @Authorize({ roles: [Role.developer, Role.recyclingFund] })
+ @Authorize({ roles: [Role.developer, Role.recyclingFund, Role.municipality] })
@Query(() => VehicleConnection)
async skilavottordAllDeregisteredVehicles(
+ @CurrentUser() user: User,
@Args('first', { type: () => Int }) first: number,
@Args('after') after: string,
): Promise {
const { pageInfo, totalCount, data } =
await this.vehicleService.findAllByFilter(first, after, {
requestType: RecyclingRequestTypes.deregistered,
+ partnerId: user.role === Role.municipality ? user.partnerId : null,
})
return {
pageInfo,
diff --git a/apps/skilavottord/ws/src/app/modules/vehicle/vehicle.service.ts b/apps/skilavottord/ws/src/app/modules/vehicle/vehicle.service.ts
index 67729e02c3a9..b43765aee663 100644
--- a/apps/skilavottord/ws/src/app/modules/vehicle/vehicle.service.ts
+++ b/apps/skilavottord/ws/src/app/modules/vehicle/vehicle.service.ts
@@ -1,14 +1,15 @@
+import type { Logger } from '@island.is/logging'
+import { LOGGER_PROVIDER } from '@island.is/logging'
+import { paginate } from '@island.is/nest/pagination'
import { Inject, Injectable } from '@nestjs/common'
import { InjectModel } from '@nestjs/sequelize'
-import { paginate } from '@island.is/nest/pagination'
+import { RecyclingPartnerModel } from '../recyclingPartner'
+import { MunicipalityModel } from '../municipality/municipality.model'
import {
RecyclingRequestModel,
RecyclingRequestTypes,
} from '../recyclingRequest'
-import { RecyclingPartnerModel } from '../recyclingPartner'
import { VehicleModel } from './vehicle.model'
-import type { Logger } from '@island.is/logging'
-import { LOGGER_PROVIDER } from '@island.is/logging'
@Injectable()
export class VehicleService {
@@ -23,6 +24,31 @@ export class VehicleService {
after: string,
filter?: { requestType?: RecyclingRequestTypes; partnerId?: string },
) {
+ let partnerIds = []
+
+ // Get all sub recycling partners of the municipality
+ // else get all
+ if (filter.partnerId) {
+ try {
+ const recyclingPartners = await RecyclingPartnerModel.findAll({
+ where: {
+ municipalityId: filter.partnerId,
+ },
+ attributes: ['companyId', 'companyName', 'municipalityId'],
+ })
+
+ partnerIds = [
+ filter.partnerId,
+ ...recyclingPartners.map((partner) => partner.companyId),
+ ]
+ } catch (error) {
+ this.logger.error('Failed to fetch sub-partners:', error)
+ throw new Error('Failed to process municipality partners')
+ }
+ } else {
+ partnerIds = null
+ }
+
return paginate({
Model: this.vehicleModel,
limit: first,
@@ -34,15 +60,25 @@ export class VehicleService {
model: RecyclingRequestModel,
where: {
...(filter.requestType ? { requestType: filter.requestType } : {}),
- ...(filter.partnerId
+ ...(partnerIds
? {
- recyclingPartnerId: filter.partnerId,
+ recyclingPartnerId: partnerIds,
}
: {}),
},
include: [
{
model: RecyclingPartnerModel,
+ attributes: ['companyId', 'companyName', 'municipalityId'],
+ required: false, // Allows for left join
+ include: [
+ {
+ model: MunicipalityModel,
+ attributes: ['companyName'],
+ as: 'municipality',
+ required: false,
+ },
+ ],
},
],
},