Skip to content

Commit

Permalink
Merge pull request #36 from rotirk20/development
Browse files Browse the repository at this point in the history
Backend configuration & Frontend using backend for locations and users
  • Loading branch information
rotirk20 authored Oct 21, 2024
2 parents 1056912 + 332b819 commit 96a677c
Show file tree
Hide file tree
Showing 14 changed files with 127 additions and 64 deletions.
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');
}
}
File renamed without changes
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'
};

0 comments on commit 96a677c

Please sign in to comment.