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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 98 additions & 98 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,103 +20,6 @@ Users can search, filter and sort the artwork catalogue, browse results using pa

For the third delivery, the project represents a first complete prototype of the application, with particular focus on catalogue functionality, frontend/backend integration, accessibility and sustainable web development.

## Main functionality

The application currently supports:

- Searching artworks by title or artist
- Case-insensitive search
- Search submission through a search button to avoid unnecessary requests
- Artist filtering with debounced artist search
- Sorting by title, artist and year
- Server-side filtering and sorting
- Pagination of artwork results
- Individual artwork detail pages
- Responsive layouts for different screen sizes
- User registration and login
- Protected profile pages for authenticated users
- Creation of personal galleries
- Adding artworks to galleries
- Persistent user-generated gallery data
- Profile and cover image customization
- Loading, empty and error states for relevant functionality

Sorting, filtering and pagination are performed through the backend rather than only on the data currently loaded in the browser. This allows the solution to scale to larger datasets.

## Technologies

### Frontend

- React
- TypeScript
- Vite
- Tailwind CSS
- React Router
- Zustand
- React Select
- Lucide React

### Backend

- Node.js
- TypeScript
- GraphQL
- Apollo Server
- MongoDB

### Development tools

- ESLint
- Prettier
- Git
- GitLab

## Technical choices

The project is divided into a frontend, backend and database. The frontend is built with React and TypeScript using Vite, while the backend is built with Node.js, TypeScript, GraphQL and Apollo Server. MongoDB is used to store artwork data, users and galleries.

The frontend communicates with the backend through GraphQL. This allows the client to request only the fields it needs and makes it possible to perform pagination, search, filtering and sorting on the backend instead of loading the complete dataset into the browser.

Artwork results are loaded using pagination. The frontend sends a `limit` and `offset` to the GraphQL backend, and the backend retrieves only the requested portion of the dataset from MongoDB. The catalogue currently displays 12 artworks per page. This approach was chosen with scalability in mind, since the application should be able to handle a significantly larger dataset without transferring all artwork data to the client at once.

Filtering and sorting are also performed by the backend so that they apply to the complete result set rather than only the artworks currently displayed in the browser. The application supports sorting by title, artist and year in ascending or descending order.

The main artwork search is submitted explicitly instead of sending a request for every character typed into the search field. Artist search uses a debounce before requesting matching artists from the backend. These choices reduce unnecessary network requests while still providing a responsive user experience.

Zustand is used for shared frontend state. Authentication state, including the current user and authentication token, is stored in a central Zustand store and shared across login, registration, protected routes, profile navigation and authenticated gallery functionality. The store is persisted using `sessionStorage`, allowing the user to remain logged in when refreshing the page during the same browser session.

State that only belongs to a single component remains managed using React state. Form input, modal visibility, loading states and locally fetched gallery data are therefore kept local instead of being moved into global state unnecessarily.

Artex also includes persistent user-generated data through personal galleries. Authenticated users can create galleries and add artworks to them. Gallery information and artwork IDs are stored in MongoDB, so the data remains available after refreshing or logging in again. When an artwork is added to a gallery, set-based database updates are used to avoid storing the same artwork multiple times in the same gallery.

The design is based on our Figma prototype and is implemented responsively so that the layout adapts to different screen sizes. The frontend is divided into reusable components and hooks to separate responsibilities and make the code easier to maintain and test.

## Accessibility

Accessibility has been considered throughout the frontend implementation.

Examples of accessibility choices include semantic HTML elements such as `main`, `section`, `form`, `button` and links, labels connected to form inputs, `aria-label` for icon-only controls and accessible attributes for expandable filters and dialogs.

The catalogue search uses `role="search"`, while relevant feedback is presented using `role="alert"` and `role="status"`. Meaningful images use alternative text, while decorative images use empty alternative text where appropriate. Interactive elements use keyboard-accessible buttons and links, and visible focus styles are included for relevant controls.

The application also provides loading states, empty-result messages and error feedback so that users receive clear information about what is happening.

Accessibility will continue to be reviewed throughout the remaining development process, particularly keyboard navigation, focus handling in dialogs and general usability.

## Sustainable web development

Sustainable web development has influenced several technical and design choices in Artex.

Pagination ensures that only a limited number of artworks are retrieved at a time instead of transferring the complete database to the browser. Server-side filtering and sorting further reduce unnecessary client-side processing and data transfer.

The main artwork search is triggered by form submission rather than every keyboard input, while artist search uses debouncing. This reduces unnecessary network requests.

GraphQL allows each view to request only the fields it needs. The catalogue view therefore retrieves only the information required for artwork cards, while additional artwork information is fetched when the user opens an individual detail page.

Artwork listings use smaller artwork image variants where available rather than unnecessarily requesting higher-resolution images. Missing images are handled using placeholders.

Together, these choices reduce network traffic, browser memory usage and unnecessary client-side computation while also improving the scalability of the application.

## Getting started

Before running the project, make sure Node.js and npm are installed.
Expand Down Expand Up @@ -225,6 +128,103 @@ Testing will be expanded further towards the final delivery. The final project w

For the third delivery, the primary focus has been completing and validating the functional prototype, including search, pagination, filtering, sorting, authentication, galleries, accessibility and sustainable development choices.

## Main functionality

The application currently supports:

- Searching artworks by title or artist
- Case-insensitive search
- Search submission through a search button to avoid unnecessary requests
- Artist filtering with debounced artist search
- Sorting by title, artist and year
- Server-side filtering and sorting
- Pagination of artwork results
- Individual artwork detail pages
- Responsive layouts for different screen sizes
- User registration and login
- Protected profile pages for authenticated users
- Creation of personal galleries
- Adding artworks to galleries
- Persistent user-generated gallery data
- Profile and cover image customization
- Loading, empty and error states for relevant functionality

Sorting, filtering and pagination are performed through the backend rather than only on the data currently loaded in the browser. This allows the solution to scale to larger datasets.

## Technologies

### Frontend

- React
- TypeScript
- Vite
- Tailwind CSS
- React Router
- Zustand
- React Select
- Lucide React

### Backend

- Node.js
- TypeScript
- GraphQL
- Apollo Server
- MongoDB

### Development tools

- ESLint
- Prettier
- Git
- GitLab

## Technical choices

The project is divided into a frontend, backend and database. The frontend is built with React and TypeScript using Vite, while the backend is built with Node.js, TypeScript, GraphQL and Apollo Server. MongoDB is used to store artwork data, users and galleries.

The frontend communicates with the backend through GraphQL. This allows the client to request only the fields it needs and makes it possible to perform pagination, search, filtering and sorting on the backend instead of loading the complete dataset into the browser.

Artwork results are loaded using pagination. The frontend sends a `limit` and `offset` to the GraphQL backend, and the backend retrieves only the requested portion of the dataset from MongoDB. The catalogue currently displays 12 artworks per page. This approach was chosen with scalability in mind, since the application should be able to handle a significantly larger dataset without transferring all artwork data to the client at once.

Filtering and sorting are also performed by the backend so that they apply to the complete result set rather than only the artworks currently displayed in the browser. The application supports sorting by title, artist and year in ascending or descending order.

The main artwork search is submitted explicitly instead of sending a request for every character typed into the search field. Artist search uses a debounce before requesting matching artists from the backend. These choices reduce unnecessary network requests while still providing a responsive user experience.

Zustand is used for shared frontend state. Authentication state, including the current user and authentication token, is stored in a central Zustand store and shared across login, registration, protected routes, profile navigation and authenticated gallery functionality. The store is persisted using `sessionStorage`, allowing the user to remain logged in when refreshing the page during the same browser session.

State that only belongs to a single component remains managed using React state. Form input, modal visibility, loading states and locally fetched gallery data are therefore kept local instead of being moved into global state unnecessarily.

Artex also includes persistent user-generated data through personal galleries. Authenticated users can create galleries and add artworks to them. Gallery information and artwork IDs are stored in MongoDB, so the data remains available after refreshing or logging in again. When an artwork is added to a gallery, set-based database updates are used to avoid storing the same artwork multiple times in the same gallery.

The design is based on our Figma prototype and is implemented responsively so that the layout adapts to different screen sizes. The frontend is divided into reusable components and hooks to separate responsibilities and make the code easier to maintain and test.

## Accessibility

Accessibility has been considered throughout the frontend implementation.

Examples of accessibility choices include semantic HTML elements such as `main`, `section`, `form`, `button` and links, labels connected to form inputs, `aria-label` for icon-only controls and accessible attributes for expandable filters and dialogs.

The catalogue search uses `role="search"`, while relevant feedback is presented using `role="alert"` and `role="status"`. Meaningful images use alternative text, while decorative images use empty alternative text where appropriate. Interactive elements use keyboard-accessible buttons and links, and visible focus styles are included for relevant controls.

The application also provides loading states, empty-result messages and error feedback so that users receive clear information about what is happening.

Accessibility will continue to be reviewed throughout the remaining development process, particularly keyboard navigation, focus handling in dialogs and general usability.

## Sustainable web development

Sustainable web development has influenced several technical and design choices in Artex.

Pagination ensures that only a limited number of artworks are retrieved at a time instead of transferring the complete database to the browser. Server-side filtering and sorting further reduce unnecessary client-side processing and data transfer.

The main artwork search is triggered by form submission rather than every keyboard input, while artist search uses debouncing. This reduces unnecessary network requests.

GraphQL allows each view to request only the fields it needs. The catalogue view therefore retrieves only the information required for artwork cards, while additional artwork information is fetched when the user opens an individual detail page.

Artwork listings use smaller artwork image variants where available rather than unnecessarily requesting higher-resolution images. Missing images are handled using placeholders.

Together, these choices reduce network traffic, browser memory usage and unnecessary client-side computation while also improving the scalability of the application.

## Use of AI

AI has been used as a supporting tool during the development of Artex.
Expand All @@ -242,4 +242,4 @@ AI is therefore used primarily as a development and learning aid rather than as
- Emma Fredriksen
- Mina Ellingsen
- Karen Skauge
- Gurveer Singh
- Gurveer Singh
2 changes: 1 addition & 1 deletion frontend/src/components/AddToGalleryModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function AddToGalleryModal({
`;

try {
const response = await fetch("/graphql", {
const response = await fetch(`${import.meta.env.BASE_URL}graphql`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/CreateGalleryModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function CreateGalleryModal({
`;

try {
const response = await fetch("/graphql", {
const response = await fetch(`${import.meta.env.BASE_URL}graphql`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down
21 changes: 12 additions & 9 deletions frontend/src/hooks/useGalleryCoverImages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,19 @@ export function useGalleryCoverImages(galleries: Gallery[]) {
`;

try {
const response = await fetch("/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
const response = await fetch(
`${import.meta.env.BASE_URL}graphql`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
query,
variables: { id: artworkId },
}),
},
body: JSON.stringify({
query,
variables: { id: artworkId },
}),
});
);

if (!response.ok) {
throw new Error("Could not fetch gallery cover image");
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/CreateAccountPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default function CreateAccount() {
`;

try {
const response = await fetch("/graphql", {
const response = await fetch(`${import.meta.env.BASE_URL}graphql`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, variables: { username, password } }),
Expand Down Expand Up @@ -72,7 +72,7 @@ export default function CreateAccount() {
}
`;

const loginResponse = await fetch("/graphql", {
const loginResponse = await fetch(`${import.meta.env.BASE_URL}graphql`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function Login() {
`;

try {
const response = await fetch("/graphql", {
const response = await fetch(`${import.meta.env.BASE_URL}graphql`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, variables: { username, password } }),
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/ProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default function ProfilePage() {
`;

try {
const response = await fetch("/graphql", {
const response = await fetch(`${import.meta.env.BASE_URL}graphql`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down