Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backend configuration & Frontend using backend for locations and users #36

Merged
merged 4 commits into from
Oct 21, 2024
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
5 changes: 4 additions & 1 deletion backend/config/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const dotenv = require('dotenv');

dotenv.config();

const sequelize = new Sequelize(process.env.DATABASE_URL, {
const isProduction = process.env.NODE_ENV === 'production';
const databaseUrl = isProduction ? process.env.DATABASE_URL : process.env.DEV_DATABASE_URL;

const sequelize = new Sequelize(databaseUrl, {
dialect: 'postgres',
logging: false,
});
Expand Down
15 changes: 14 additions & 1 deletion backend/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,22 @@ const startServer = async () => {
await initModels();

const PORT = process.env.PORT || 5100;
app.listen(PORT, () => {
const FALLBACK_PORT = 5200;

const server = app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`Port ${PORT} is already in use. Trying port ${FALLBACK_PORT}...`);
app.listen(FALLBACK_PORT, () => {
console.log(`Server is running on port ${FALLBACK_PORT}`);
});
} else {
console.error('Server error:', err);
}
});
};

startServer();
52 changes: 26 additions & 26 deletions frontend/src/app/admin/components/table/table.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,33 @@
<td *ngFor="let column of columns" class="border-t-0 px-6 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap p-4">
<ng-container *ngIf="column.key !== 'actions'; else actionButtons">
{{ row[column.key] }}
</ng-container>
<!-- Action buttons template -->
<ng-template #actionButtons>
<div class="inline-flex items-center rounded-md shadow-sm">
<button class="text-white text-sm bg-green-600 hover:bg-green-500 border border-slate-200 rounded-l-lg font-medium px-4 py-2 inline-flex space-x-1 items-center">
<span><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
</svg>
</span>
</button>
<button class="text-white bg-blue-600 text-sm hover:bg-blue-500 border-y border-slate-200 font-medium px-4 py-2 inline-flex space-x-1 items-center" *ngIf="viewAction">
<span>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</ng-container>
<!-- Action buttons template -->
<ng-template #actionButtons>
<div class="inline-flex items-center rounded-md shadow-sm">
<button class="text-white text-sm bg-green-600 hover:bg-green-500 border border-slate-200 rounded-l-lg font-medium px-4 py-2 inline-flex space-x-1 items-center">
<span><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
</svg>
</span>
</button>
<button class="text-white text-sm bg-red-600 hover:bg-red-500 border border-slate-200 rounded-r-lg font-medium px-4 py-2 inline-flex space-x-1 items-center">
<span>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>
</span>
</button>
</div>
</ng-template>
</button>
<button class="text-white bg-blue-600 text-sm hover:bg-blue-500 border-y border-slate-200 font-medium px-4 py-2 inline-flex space-x-1 items-center" *ngIf="viewAction">
<span>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</span>
</button>
<button class="text-white text-sm bg-red-600 hover:bg-red-500 border border-slate-200 rounded-r-lg font-medium px-4 py-2 inline-flex space-x-1 items-center">
<span>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>
</span>
</button>
</div>
</ng-template>
</td>
</tr>
</tbody>
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<p>Lokacije</p>
<app-table [data]="locations" [columns]="columns"></app-table>
28 changes: 27 additions & 1 deletion frontend/src/app/admin/pages/locations/locations.component.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
import { Location } from 'src/app/shared/models/location.model';
import { LocationService } from 'src/app/shared/services/location.service';
import { TableComponent } from '../../components/table/table.component';

@Component({
selector: 'app-locations',
standalone: true,
imports: [RouterLink],
imports: [RouterLink, TableComponent],
templateUrl: './locations.component.html',
styleUrls: ['./locations.component.scss']
})
export class LocationsComponent {
columns = [
{ key: 'id', label: 'ID' },
{ key: 'image', label: 'Slika' },
{ key: 'name', label: 'Ime' },
{ key: 'description', label: 'Opis' },
{ key: 'actions', label: 'Akcije' }
];
locations: Location[] = [];
constructor(private locationService: LocationService) {}

ngOnInit(): void {
this.getUsers();
}

getUsers() {
this.locationService.getLocations().subscribe({
next: (locations: Location[]) => {
this.locations = locations;
},
error: err => console.error('Observable emitted an error: ' + err),
complete: () => console.log('Observable emitted the complete notification')
});
}
onClick() {
console.log('Link clicked!');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8">
<article *ngFor="let item of data"
class="relative isolate flex flex-col justify-end overflow-hidden rounded-2xl px-8 pb-8 pt-40 w-full mx-auto cursor-pointer shadow-lg">
<img src="{{item.image}}" alt="{{item.name}}" class="absolute inset-0 h-full w-full object-cover">
<img src="../../../../assets/images/{{item.image}}" alt="{{item.name}}" class="absolute inset-0 h-full w-full object-cover">
<div class="absolute inset-0 bg-gradient-to-t from-gray-900 via-gray-900/40"></div>
<h3 class="z-10 mt-3 text-2xl font-bold text-white">{{item.name}}</h3>
</article>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ <h3 class="font-semibold mb-2 uppercase">
<div class="sm:flex sm:space-x-6">
<!-- Image -->
<img class="block h-32 w-32 object-cover rounded-md sm:mx-0 sm:flex-shrink-0"
[src]="location.image" alt="{{ location.name }}">
src="../../../../assets/images/{{location.image}}" alt="{{ location.name }}">

<!-- Description and Type -->
<div class="space-y-2 flex-1 location-detail mt-2 sm:mt-0">
<!-- <span class="text-gray-600 text-slate-500 tracking-widest location-type">
{{ location.type }}
</span> -->
<span class="inline-flex items-center bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 tracking-widest location-type">{{ location.type }}</span>
<span class="inline-flex items-center bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 tracking-widest location-type">{{ location.Category.name }}</span>
<p class="text-gray-600 text-sm text-slate-500 description">
{{ location.description | slice:0:300 }}
</p>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/app/home/pages/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Title, Meta } from '@angular/platform-browser';
})
export class HomeComponent implements OnInit {
locations: Location[] = [];

constructor(private locationService: LocationService, private title: Title, private meta: Meta) {
this.title.setTitle('Početna - Dobro došli');
this.meta.addTags([
Expand All @@ -25,7 +26,7 @@ export class HomeComponent implements OnInit {

ngOnInit() {
this.locationService.getLocations().subscribe(data => {
this.locations = data.locations; // Assuming 'locations' is the key in your JSON
this.locations = data; // Assuming 'locations' is the key in your JSON
});
}
}
42 changes: 23 additions & 19 deletions frontend/src/app/home/pages/location/location.component.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
<main class="flex items-center justify-center bg-gray-100">
<div class="h-screen w-full flex items-center justify-center" *ngIf="loading">
<div class="w-12 h-12 rounded-full animate-spin border-4 border-solid border-blue-500 border-t-transparent"></div>
</div>
<main class="flex items-center justify-center bg-gray-100" *ngIf="!loading">
<div class="w-full lg:flex container">
<div class="py-6 px-4">
<ng-container *ngFor="let locationItem of locations; let i = index">
<app-location-card
[location]="locationItem"
[index]="i"
[isSelected]="isLocationSelected(i)"
(locationSelected)="openInfoWindowFromCard(i)"
(click)="scrollToMap()">
</app-location-card>
</ng-container>
<div *ngIf="locations.length > 0; else noData">
<ng-container *ngFor="let locationItem of locations; let i = index">
<app-location-card [location]="locationItem" [index]="i" [isSelected]="isLocationSelected(i)"
(locationSelected)="openInfoWindowFromCard(i)" (click)="scrollToMap()">
</app-location-card>
</ng-container>
</div>
<ng-template #noData>
<div class="w-full md:w-96">
<p class="bg-zinc-50 text-gray-700 p-4 rounded-lg text-center font-semibold">
Nema podataka.
</p>
</div>
</ng-template>
</div>
<div class="w-full py-6" #googleMapContainer>
<google-map height="100vh" width="100%" [zoom]="zoom" [center]="center" [options]="options">
<map-marker
*ngFor="let marker of markers; let i = index"
[position]="marker.position"
[options]="getMarkerOptions(i)"
#markerRef="mapMarker"
(mapClick)="openInfoWindow(marker, markerRef, i)">
<map-marker *ngFor="let marker of markers; let i = index" [position]="marker.position"
[options]="getMarkerOptions(i)" #markerRef="mapMarker" (mapClick)="openInfoWindow(marker, markerRef, i)">
</map-marker>
<map-info-window #infoWindow>
<div class="marker-info shadow-md">
<img [src]="infoWindowContent.image" />
<div class="marker-info shadow-md" *ngIf="infoWindowContent?.image">
<img src="../../../../assets/images/{{infoWindowContent.image}}" />
<div class="marker-details">
<h3 class="text-lg font-semibold text-center">{{ infoWindowContent.name }}</h3>
</div>
Expand All @@ -31,4 +35,4 @@ <h3 class="text-lg font-semibold text-center">{{ infoWindowContent.name }}</h3>
</google-map>
</div>
</div>
</main>
</main>
21 changes: 15 additions & 6 deletions frontend/src/app/home/pages/location/location.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class LocationComponent implements OnInit {
};
zoom = 12;
markers: Marker[] = [];
loading: boolean = true;
options: google.maps.MapOptions = {
mapTypeId: 'hybrid',
zoomControl: true,
Expand Down Expand Up @@ -59,17 +60,25 @@ export class LocationComponent implements OnInit {
constructor(private locationService: LocationService) {}

ngOnInit() {
this.locationService.getLocations().subscribe(data => {
this.locations = data.locations; // Assuming 'locations' is the key in your JSON
this.loadMarkers();
})
this.locationService.getLocations().subscribe({
next: (locations: Location[]) => {
this.locations = locations;
this.loadMarkers();
},
error: err => this.loading = false,
complete: () => this.loading = false
});
}

loadMarkers() {
this.locations.forEach(location => {
this.locations.forEach((location: Location) => {
let coordinates = {
lat: location.latitude,
lng: location.longitude
}
this.markers.push(
{
position: location.coordinates,
position: coordinates,
title: location.name,
image: location.image
},
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/app/shared/models/location.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@ export interface WorkingHours {
close: string; // e.g., "17:00"
}
export interface Location {
latitude: number;
longitude: number,
id: number;
name: string;
description: string,
address: string;
type: string;
image?: string;
coordinates: google.maps.LatLngLiteral;
Category: {
name: string,
id: number
}
}

export interface LocationWithHours extends Location {
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/app/shared/services/location.service.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { HttpClient } from '@angular/common/http';
import { Location } from '../models/location.model';
import { environment } from 'src/environments/environment';

@Injectable({
providedIn: 'root'
})
export class LocationService {
private jsonUrl = 'assets/data/data.json'; // Path to your JSON file
private apiUrl = environment.apiUrl;

constructor(private http: HttpClient) {}

getLocations(): Observable<any> {
return this.http.get(this.jsonUrl);
getLocations(): Observable<Location[]> {
return this.http.get<Location[]>(this.apiUrl + '/locations');
}
}
2 changes: 1 addition & 1 deletion frontend/src/environments/environment.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const environment = {
production: false,
apiUrl: 'http://localhost:5500/api'
apiUrl: 'http://localhost:4000/api'
};
Loading