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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -288,5 +288,13 @@
},
"tags": []
}
},
"schematics": {
"@angular-eslint/schematics:application": {
"setParserOptionsProject": true
},
"@angular-eslint/schematics:library": {
"setParserOptionsProject": true
}
}
}
5 changes: 2 additions & 3 deletions apps/frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { HttpClientModule } from '@angular/common/http'
import { FooterComponent } from './components/footer/footer.component'
import { FaIconLibrary, FontAwesomeModule } from '@fortawesome/angular-fontawesome'
import { FormsModule } from '@angular/forms'
import { NxModule } from '@nrwl/angular'
import { ActionReducer, Store, StoreModule } from '@ngrx/store'
import { StoreDevtoolsModule } from '@ngrx/store-devtools'
import { EffectsModule } from '@ngrx/effects'
Expand All @@ -20,7 +19,7 @@ import { ToastComponent } from './components/toast/toast.component'
import { faFacebookF } from '@fortawesome/free-brands-svg-icons'
import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component'
import { LayoutModule } from '@angular/cdk/layout'
import { APOLLO_OPTIONS } from 'apollo-angular'
import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular'
import { HttpLink } from 'apollo-angular/http'
import { InMemoryCache } from '@apollo/client/core'
import { createPersistedQueryLink } from 'apollo-angular/persisted-queries'
Expand Down Expand Up @@ -48,13 +47,13 @@ export const NGRX_STATE = makeStateKey('NGRX_STATE')
FontAwesomeModule,
FormsModule,
LoadersModule,
NxModule.forRoot(),
StoreModule.forRoot({}, { metaReducers: [stateSetter] }),
EffectsModule.forRoot([]),
StoreDevtoolsModule.instrument({
maxAge: 25,
}),
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
ApolloModule,
],
providers: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ import { TestBed } from '@angular/core/testing'

import { CanteenService } from './canteen.service'
import { of } from 'rxjs'
import { HttpClient } from '@angular/common/http'
import { Apollo } from 'apollo-angular'

const HttpMock = {
get: () => of([]),
const ApolloMock = {
query: () => of({ data: { w1: [], w2: [] } }),
}

describe('CanteenService', () => {
beforeEach(() =>
TestBed.configureTestingModule({
providers: [
{
provide: HttpClient,
useValue: HttpMock,
provide: Apollo,
useValue: ApolloMock,
},
],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ import { EffectsModule } from '@ngrx/effects'
import { StoreModule } from '@ngrx/store'
import { provideMockActions } from '@ngrx/effects/testing'

import { DataPersistence, NxModule } from '@nrwl/angular'

import { EventsEffects } from './events.effects'
import { HttpClient } from '@angular/common/http'
import { RequestService } from '../services/request.service'

const httpMock = {}
const RequestServiceMock = {
getEvents: () => of([]),
}

describe('EventsEffects', () => {
const actions: Observable<any> = of({})
let effects: EventsEffects

beforeEach(() => {
TestBed.configureTestingModule({
imports: [NxModule.forRoot(), StoreModule.forRoot({}), EffectsModule.forRoot([])],
providers: [EventsEffects, DataPersistence, provideMockActions(() => actions), { provide: HttpClient, useValue: httpMock }],
imports: [StoreModule.forRoot({}), EffectsModule.forRoot([])],
providers: [EventsEffects, provideMockActions(() => actions), { provide: RequestService, useValue: RequestServiceMock }],
})

effects = TestBed.inject(EventsEffects)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { TestBed } from '@angular/core/testing'

import { RequestService } from './request.service'
import { HttpClient } from '@angular/common/http'
import { Apollo } from 'apollo-angular'
import { of } from 'rxjs'

const httpClientMock = {}
const ApolloMock = {
query: () => of({ data: { events: [] } }),
}

describe('RequestService', () => {
beforeEach(() =>
TestBed.configureTestingModule({
providers: [{ provide: HttpClient, useValue: httpClientMock }],
providers: [{ provide: Apollo, useValue: ApolloMock }],
})
)

Expand Down
94 changes: 60 additions & 34 deletions apps/frontend/src/app/modules/home/services/request.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core'
import {BehaviorSubject, Observable, Subject} from 'rxjs'
import { BehaviorSubject, Observable, Subject } from 'rxjs'
import { Post } from '../../../models/Post'
import {Apollo, gql, QueryRef} from "apollo-angular";
import {map, take} from "rxjs/operators";
import { Apollo, gql, QueryRef } from 'apollo-angular'
import { map, take } from 'rxjs/operators'

const QUERY = gql`
query Posts($featured: Boolean, $after: String, $before: String, $first: Int, $last: Int) {
Expand Down Expand Up @@ -57,7 +57,12 @@ export class RequestService {
listQuery: QueryRef<Result>
posts?: Post[]
pageInfo?: PageInfo
pageInfo$: Subject<PageInfo> = new BehaviorSubject<PageInfo>({hasPreviousPage: true, hasNextPage: false, startCursor: "", endCursor: ""})
pageInfo$: Subject<PageInfo> = new BehaviorSubject<PageInfo>({
hasPreviousPage: true,
hasNextPage: false,
startCursor: '',
endCursor: '',
})

constructor(private gql: Apollo) {
this.listQuery = this.gql.watchQuery<Result>({
Expand All @@ -66,58 +71,79 @@ export class RequestService {
featured: false,
last: 20,
},
fetchPolicy: 'network-only'
fetchPolicy: 'network-only',
})

const s = this.listQuery.valueChanges.subscribe(res => {
const s = this.listQuery.valueChanges.subscribe((res) => {
this.pageInfo = res.data.posts.pageInfo
this.pageInfo$.next(this.pageInfo)
s.unsubscribe()
})
}

async fetchMore() {
await this.listQuery.fetchMore({
if (!this.pageInfo) {
return
}

const result = await this.listQuery.fetchMore({
variables: {
last: 20,
before: this.pageInfo.endCursor
before: this.pageInfo.endCursor,
Copy link

Copilot AI Jan 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchMore method accesses this.pageInfo.endCursor without checking if this.pageInfo is defined. If fetchMore is called before the initial subscription in the constructor completes, this will throw a runtime error. Consider adding a null check or ensuring pageInfo is initialized before allowing fetchMore to be called.

Copilot uses AI. Check for mistakes.
},
updateQuery: (prev, { fetchMoreResult }) => {
this.pageInfo = fetchMoreResult.posts.pageInfo
this.pageInfo$.next(this.pageInfo)
})

return {
posts: {
edges: [...prev.posts.edges, ...fetchMoreResult.posts.edges],
pageInfo: fetchMoreResult.posts.pageInfo
}
}
if (result.data) {
this.pageInfo = result.data.posts.pageInfo
this.pageInfo$.next(this.pageInfo)

// Update cache with merged results
const prev = this.listQuery.getCurrentResult().data
if (prev) {
this.gql.client.cache.writeQuery({
query: QUERY,
variables: {
featured: false,
last: 20,
},
data: {
posts: {
__typename: 'PostConnection',
edges: [...prev.posts.edges, ...result.data.posts.edges],
pageInfo: result.data.posts.pageInfo,
},
},
})
Comment on lines +103 to +116
Copy link

Copilot AI Jan 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manual cache update in the fetchMore implementation uses hardcoded variables that may not match the current query state. The query was initialized with last: 20 and featured: false, but after calling fetchMore, the cache is being written with these same hardcoded values rather than preserving the actual query variables. This could lead to cache inconsistencies if the query variables are different. Consider using the original query's variables or ensuring the cache write matches the actual query state.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +116
Copy link

Copilot AI Jan 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache update in fetchMore uses hardcoded variables (featured: false, last: 20) that may not match the actual query variables. If the query was initiated with different variables, this cache write could create inconsistencies. Consider extracting the original query variables and reusing them, or use the listQuery.options.variables property to ensure consistency between the query and cache write operations.

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +116
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use the relayStylePagination() field policy from @apollo/client?

}
})
}
}

hasPreviousPage(): Observable<boolean> {
return this.pageInfo$.pipe(map(e => e.hasPreviousPage))
return this.pageInfo$.pipe(map((e) => e.hasPreviousPage))
}

listPosts(): Observable<Post[]> {
return this.listQuery.valueChanges.pipe(map(res => {
return res.data.posts.edges.map(edge => edge.node)
}))
return this.listQuery.valueChanges.pipe(
map((res) => {
return res.data.posts.edges.map((edge) => edge.node)
})
)
}

listFeaturedPosts(): Observable<Post[]> {
return this.gql.query<Result>({
query: QUERY,
variables: {
featured: true,
last: 20
}
}).pipe(
map(res => {
return res.data.posts.edges.map(edge => edge.node)
}),
take(1)
)
return this.gql
.query<Result>({
query: QUERY,
variables: {
featured: true,
last: 20,
},
})
.pipe(
map((res) => {
return res.data.posts.edges.map((edge) => edge.node)
}),
take(1)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { EffectsModule } from '@ngrx/effects'
import { StoreModule } from '@ngrx/store'
import { provideMockActions } from '@ngrx/effects/testing'

import { DataPersistence, NxModule } from '@nrwl/angular'

import { ColleaguesEffects } from './colleagues.effects'
import { ColleaguesService } from '../../services/colleagues.service'

Expand All @@ -23,10 +21,9 @@ describe('ColleaguesEffects', () => {

beforeEach(() => {
TestBed.configureTestingModule({
imports: [NxModule.forRoot(), StoreModule.forRoot({}), EffectsModule.forRoot([])],
imports: [StoreModule.forRoot({}), EffectsModule.forRoot([])],
providers: [
ColleaguesEffects,
DataPersistence,
provideMockActions(() => actions),
{
provide: ColleaguesService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ import { TestBed } from '@angular/core/testing'

import { ColleaguesService } from './colleagues.service'
import { of } from 'rxjs'
import { HttpClient } from '@angular/common/http'
import { Apollo } from 'apollo-angular'

const HttpMock = {
get: () => of([]),
const ApolloMock = {
query: () => of({ data: { colleagues: [] } }),
}

describe('ColleaguesService', () => {
beforeEach(() => {
return TestBed.configureTestingModule({
providers: [
{
provide: HttpClient,
useValue: HttpMock,
provide: Apollo,
useValue: ApolloMock,
},
],
})
Expand Down
15 changes: 0 additions & 15 deletions apps/frontend/src/hmr.ts

This file was deleted.

21 changes: 5 additions & 16 deletions apps/frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,12 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { AppModule } from './app/app.module'
import { environment } from './environments/environment'

import { hmrBootstrap } from './hmr'

if (environment.production) {
enableProdMode()
}

const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule)

if (environment.hmr) {
if (module['hot']) {
hmrBootstrap(module, bootstrap)
} else {
console.error('HMR is not enabled for webpack-dev-server!')
console.log('Are you using the --hmr flag for ng serve?')
}
} else {
document.addEventListener('DOMContentLoaded', () => {
bootstrap().catch((err) => console.log(err))
})
}
document.addEventListener('DOMContentLoaded', () => {
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) => console.error(err))
})
2 changes: 1 addition & 1 deletion apps/frontend/src/polyfills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone' // Included with Angular CLI.
import 'zone.js' // Included with Angular CLI.

/***************************************************************************************************
* APPLICATION IMPORTS
Expand Down
9 changes: 2 additions & 7 deletions apps/frontend/src/test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js/dist/zone-testing'
import 'zone.js'
import 'zone.js/testing'
import { getTestBed } from '@angular/core/testing'
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'

declare const require: any

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false },
})
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/)
// And load the modules.
context.keys().map(context)
11 changes: 2 additions & 9 deletions libs/calendar/src/test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js/dist/zone'

import 'zone.js/dist/zone-testing'
import 'zone.js'
import 'zone.js/testing'
import { getTestBed } from '@angular/core/testing'
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'

declare const require: any

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false },
})
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/)
// And load the modules.
context.keys().map(context)
Loading