rework
This commit is contained in:
9
front/v2/src/apiContracts/dataApi.ts
Normal file
9
front/v2/src/apiContracts/dataApi.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export class dataApi {
|
||||
static readonly Photos = {
|
||||
getAll: '/photos',
|
||||
getById: (id: string) => `/photos/${id}`,
|
||||
create: '/photos',
|
||||
update: (id: string) => `/photos/${id}`,
|
||||
delete: (id: string) => `/photos/${id}`,
|
||||
};
|
||||
}
|
12
front/v2/src/app/app.config.ts
Normal file
12
front/v2/src/app/app.config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideBrowserGlobalErrorListeners(),
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(routes)
|
||||
]
|
||||
};
|
2
front/v2/src/app/app.html
Normal file
2
front/v2/src/app/app.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<custom-header></custom-header>
|
||||
<router-outlet></router-outlet>
|
23
front/v2/src/app/app.routes.ts
Normal file
23
front/v2/src/app/app.routes.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { HomeView } from './views/home-view/home-view';
|
||||
import { LoginView } from './views/login-view/login-view';
|
||||
import { UserProfileView } from './views/user-profile-view/user-profile-view';
|
||||
import { AdminView } from './views/admin-view/admin-view';
|
||||
import { ContentManagerView } from './views/content-manager-view/content-manager-view';
|
||||
import { ServicesView } from './views/services-view/services-view';
|
||||
import { UserGalleriesView } from './views/user-galleries-view/user-galleries-view';
|
||||
import { TagsView } from './views/tags-view/tags-view';
|
||||
import { EventsView } from './views/events-view/events-view';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: HomeView },
|
||||
{ path: 'login', component: LoginView },
|
||||
{ path: 'tags', component: TagsView },
|
||||
{ path: 'events', component: EventsView },
|
||||
{ path: 'services', component: ServicesView },
|
||||
{ path: 'my-profile', component: UserProfileView },
|
||||
{ path: 'my-galleries', component: UserGalleriesView },
|
||||
{ path: 'content-management', component: ContentManagerView },
|
||||
{ path: 'admin', component: AdminView },
|
||||
{ path: '**', redirectTo: '' },
|
||||
];
|
0
front/v2/src/app/app.scss
Normal file
0
front/v2/src/app/app.scss
Normal file
23
front/v2/src/app/app.spec.ts
Normal file
23
front/v2/src/app/app.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { App } from './app';
|
||||
|
||||
describe('App', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [App],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, v2');
|
||||
});
|
||||
});
|
14
front/v2/src/app/app.ts
Normal file
14
front/v2/src/app/app.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { Header } from './global-components/header/header';
|
||||
import { HomeView } from './views/home-view/home-view';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [Header, HomeView, RouterOutlet],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.scss',
|
||||
})
|
||||
export class App {
|
||||
protected isHomeView = true;
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
<div class="admin-panel-link">
|
||||
<a href="/admin">Admin</a>
|
||||
</div>
|
@@ -0,0 +1,79 @@
|
||||
@use "../../../styles/variables" as *;
|
||||
|
||||
.admin-panel-link {
|
||||
a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1.5rem;
|
||||
text-decoration: none;
|
||||
color: $text-dark;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.5px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
// Hover effect with special admin color
|
||||
&:hover {
|
||||
color: $warning;
|
||||
background: rgba($warning, 0.08);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba($warning, 0.15);
|
||||
}
|
||||
|
||||
// Active state
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 4px rgba($warning, 0.2);
|
||||
}
|
||||
|
||||
// Focus state for accessibility
|
||||
&:focus {
|
||||
outline: 2px solid $warning;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
// Subtle animation on hover with admin color
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba($warning, 0.1),
|
||||
transparent
|
||||
);
|
||||
transition: left 0.5s ease;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@media (max-width: 768px) {
|
||||
.admin-panel-link {
|
||||
a {
|
||||
padding: 0.6rem 1.2rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.admin-panel-link {
|
||||
a {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.25px;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminPanelLink } from './admin-panel-link';
|
||||
|
||||
describe('AdminPanelLink', () => {
|
||||
let component: AdminPanelLink;
|
||||
let fixture: ComponentFixture<AdminPanelLink>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AdminPanelLink]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AdminPanelLink);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'admin-panel-link',
|
||||
imports: [],
|
||||
templateUrl: './admin-panel-link.html',
|
||||
styleUrl: './admin-panel-link.scss'
|
||||
})
|
||||
export class AdminPanelLink {
|
||||
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
<div class="content-manager-panel-link">
|
||||
<a href="/content-manager">Contenido</a>
|
||||
</div>
|
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContentManagerPanelLink } from './content-manager-panel-link';
|
||||
|
||||
describe('ContentManagerPanelLink', () => {
|
||||
let component: ContentManagerPanelLink;
|
||||
let fixture: ComponentFixture<ContentManagerPanelLink>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContentManagerPanelLink]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ContentManagerPanelLink);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'content-manager-panel-link',
|
||||
imports: [],
|
||||
templateUrl: './content-manager-panel-link.html',
|
||||
styleUrl: './content-manager-panel-link.scss'
|
||||
})
|
||||
export class ContentManagerPanelLink {
|
||||
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
<svg-button
|
||||
label="Eventos"
|
||||
routerLink="/events"
|
||||
textAtRight="Eventos"
|
||||
icon="assets/icons/calendar-day-svgrepo-com.svg"
|
||||
></svg-button>
|
After Width: | Height: | Size: 143 B |
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EventsLink } from './events-link';
|
||||
|
||||
describe('EventsLink', () => {
|
||||
let component: EventsLink;
|
||||
let fixture: ComponentFixture<EventsLink>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [EventsLink]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(EventsLink);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { SvgButton } from '../../../utils/svg-button/svg-button';
|
||||
|
||||
@Component({
|
||||
selector: 'events-link',
|
||||
imports: [SvgButton],
|
||||
templateUrl: './events-link.html',
|
||||
styleUrl: './events-link.scss',
|
||||
})
|
||||
export class EventsLink {}
|
17
front/v2/src/app/global-components/header/header.html
Normal file
17
front/v2/src/app/global-components/header/header.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<header>
|
||||
<logo></logo>
|
||||
<events-link></events-link>
|
||||
<tags-link></tags-link>
|
||||
<services-link></services-link>
|
||||
@if (user.isLoggedIn) {
|
||||
<user-galleries-link></user-galleries-link>
|
||||
} @if (user.isContentManager) {
|
||||
<content-manager-panel-link></content-manager-panel-link>
|
||||
} @if (user.isAdmin) {
|
||||
<admin-panel-link></admin-panel-link>
|
||||
} @if (user.isLoggedIn) {
|
||||
<user-profile-link></user-profile-link>
|
||||
} @else {
|
||||
<login-link></login-link>
|
||||
}
|
||||
</header>
|
25
front/v2/src/app/global-components/header/header.scss
Normal file
25
front/v2/src/app/global-components/header/header.scss
Normal file
@@ -0,0 +1,25 @@
|
||||
header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 7%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
align-content: center;
|
||||
justify-content: space-evenly;
|
||||
gap: 0;
|
||||
|
||||
logo {
|
||||
width: 10%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
order: 0;
|
||||
}
|
||||
}
|
23
front/v2/src/app/global-components/header/header.spec.ts
Normal file
23
front/v2/src/app/global-components/header/header.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Header } from './header';
|
||||
|
||||
describe('Header', () => {
|
||||
let component: Header;
|
||||
let fixture: ComponentFixture<Header>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Header]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(Header);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
45
front/v2/src/app/global-components/header/header.ts
Normal file
45
front/v2/src/app/global-components/header/header.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Logo } from '../logo/logo';
|
||||
import { EventsLink } from '../events-link/events-link';
|
||||
import { UserGalleriesLink } from '../user-galleries-link/user-galleries-link';
|
||||
import { ContentManagerPanelLink } from '../content-manager-panel-link/content-manager-panel-link';
|
||||
import { AdminPanelLink } from '../admin-panel-link/admin-panel-link';
|
||||
import { UserProfileLink } from '../user-profile-link/user-profile-link';
|
||||
import { LoginLink } from '../login-link/login-link';
|
||||
import { TagsLink } from '../tags-link/tags-link';
|
||||
import { userService } from '../../services/userService/userService';
|
||||
import { OnInit } from '@angular/core';
|
||||
import { userModel } from '../../../models/userModel';
|
||||
import { ServicesLink } from '../services-link/services-link';
|
||||
|
||||
@Component({
|
||||
selector: 'custom-header',
|
||||
imports: [
|
||||
Logo,
|
||||
EventsLink,
|
||||
UserGalleriesLink,
|
||||
ContentManagerPanelLink,
|
||||
AdminPanelLink,
|
||||
UserProfileLink,
|
||||
LoginLink,
|
||||
TagsLink,
|
||||
ServicesLink,
|
||||
],
|
||||
templateUrl: './header.html',
|
||||
styleUrl: './header.scss',
|
||||
})
|
||||
export class Header implements OnInit {
|
||||
constructor(private userService: userService) {}
|
||||
|
||||
private currentUser: userModel = userModel.DefaultUser;
|
||||
|
||||
ngOnInit() {
|
||||
this.userService.getUser().subscribe((user) => {
|
||||
this.currentUser = user;
|
||||
});
|
||||
}
|
||||
|
||||
get user() {
|
||||
return this.currentUser;
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<svg-button
|
||||
label="login-link"
|
||||
class="login-button"
|
||||
routerLink="/login"
|
||||
textAtRight="Login"
|
||||
icon="assets/icons/door-open-svgrepo-com.svg"
|
||||
></svg-button>
|
After Width: | Height: | Size: 163 B |
@@ -0,0 +1,68 @@
|
||||
.login-button {
|
||||
// Variables para el lenguaje visual (SCSS)
|
||||
$primary-white: #fefcf8; // Blanco ahuevado gentil
|
||||
$border-grey: #e0ddd8; // Gris suave para bordes
|
||||
$text-dark: #2a2926; // Texto principal
|
||||
$standard-button-icon: #8d8d8d; // Texto principal
|
||||
$standard-button-border: #b8b8b891; // Texto principal
|
||||
$standard-button-hovered: #494949; // Texto principal
|
||||
$text-muted: #706b63; // Texto secundario
|
||||
|
||||
svg-button {
|
||||
button {
|
||||
background: $primary-white;
|
||||
border: 2px solid $standard-button-border;
|
||||
color: $standard-button-icon;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
min-width: 3.2rem;
|
||||
height: 3.2rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex-shrink: 1;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: space-evenly;
|
||||
padding: 5%;
|
||||
|
||||
label {
|
||||
text-align: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $standard-button-hovered;
|
||||
transform: scale(1.07);
|
||||
box-shadow: 0 6px 16px rgba(42, 41, 38, 0.1);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: $primary-white;
|
||||
color: $text-muted;
|
||||
}
|
||||
|
||||
svg-loader {
|
||||
box-sizing: border-box;
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
height: 80%;
|
||||
width: 80%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg-loader {
|
||||
fill: $primary-white;
|
||||
}
|
||||
label {
|
||||
color: $primary-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginLink } from './login-link';
|
||||
|
||||
describe('LoginLink', () => {
|
||||
let component: LoginLink;
|
||||
let fixture: ComponentFixture<LoginLink>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [LoginLink]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LoginLink);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
10
front/v2/src/app/global-components/login-link/login-link.ts
Normal file
10
front/v2/src/app/global-components/login-link/login-link.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { SvgButton } from '../../../utils/svg-button/svg-button';
|
||||
|
||||
@Component({
|
||||
selector: 'login-link',
|
||||
imports: [SvgButton],
|
||||
templateUrl: './login-link.html',
|
||||
styleUrl: './login-link.scss',
|
||||
})
|
||||
export class LoginLink {}
|
3
front/v2/src/app/global-components/logo/logo.html
Normal file
3
front/v2/src/app/global-components/logo/logo.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<a routerLink="/">
|
||||
<img [src]="logoSrc" alt="Logo" />
|
||||
</a>
|
6
front/v2/src/app/global-components/logo/logo.scss
Normal file
6
front/v2/src/app/global-components/logo/logo.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
a {
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
23
front/v2/src/app/global-components/logo/logo.spec.ts
Normal file
23
front/v2/src/app/global-components/logo/logo.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Logo } from './logo';
|
||||
|
||||
describe('Logo', () => {
|
||||
let component: Logo;
|
||||
let fixture: ComponentFixture<Logo>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Logo]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(Logo);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
12
front/v2/src/app/global-components/logo/logo.ts
Normal file
12
front/v2/src/app/global-components/logo/logo.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { config } from '../../../models/config';
|
||||
|
||||
@Component({
|
||||
selector: 'logo',
|
||||
imports: [],
|
||||
templateUrl: './logo.html',
|
||||
styleUrl: './logo.scss',
|
||||
})
|
||||
export class Logo {
|
||||
protected logoSrc = config.get().logoSrc;
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
<svg-button
|
||||
label="Servicios"
|
||||
routerLink="/services"
|
||||
textAtRight="Servicios"
|
||||
icon="assets/icons/book-section-svgrepo-com.svg"
|
||||
></svg-button>
|
After Width: | Height: | Size: 149 B |
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ServicesLink } from './services-link';
|
||||
|
||||
describe('ServicesLink', () => {
|
||||
let component: ServicesLink;
|
||||
let fixture: ComponentFixture<ServicesLink>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ServicesLink]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ServicesLink);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { SvgButton } from '../../../utils/svg-button/svg-button';
|
||||
|
||||
@Component({
|
||||
selector: 'services-link',
|
||||
imports: [SvgButton],
|
||||
templateUrl: './services-link.html',
|
||||
styleUrl: './services-link.scss',
|
||||
})
|
||||
export class ServicesLink {}
|
@@ -0,0 +1,6 @@
|
||||
<svg-button
|
||||
label="tags-link"
|
||||
routerLink="/tags"
|
||||
textAtRight="Etiquetas"
|
||||
icon="assets/icons/tags-svgrepo-com.svg"
|
||||
></svg-button>
|
After Width: | Height: | Size: 137 B |
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TagsLink } from './tags-link';
|
||||
|
||||
describe('TagsLink', () => {
|
||||
let component: TagsLink;
|
||||
let fixture: ComponentFixture<TagsLink>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TagsLink]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TagsLink);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
10
front/v2/src/app/global-components/tags-link/tags-link.ts
Normal file
10
front/v2/src/app/global-components/tags-link/tags-link.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { SvgButton } from '../../../utils/svg-button/svg-button';
|
||||
|
||||
@Component({
|
||||
selector: 'tags-link',
|
||||
imports: [SvgButton],
|
||||
templateUrl: './tags-link.html',
|
||||
styleUrl: './tags-link.scss',
|
||||
})
|
||||
export class TagsLink {}
|
@@ -0,0 +1,3 @@
|
||||
<div class="user-galleries-link">
|
||||
<a href="/galeria">Galería</a>
|
||||
</div>
|
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserGalleriesLink } from './user-galleries-link';
|
||||
|
||||
describe('UserGalleriesLink', () => {
|
||||
let component: UserGalleriesLink;
|
||||
let fixture: ComponentFixture<UserGalleriesLink>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserGalleriesLink]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserGalleriesLink);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'user-galleries-link',
|
||||
imports: [],
|
||||
templateUrl: './user-galleries-link.html',
|
||||
styleUrl: './user-galleries-link.scss'
|
||||
})
|
||||
export class UserGalleriesLink {
|
||||
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
<div class="user-profile-link">
|
||||
<a href="/perfil">Perfil</a>
|
||||
</div>
|
@@ -0,0 +1,79 @@
|
||||
@use "../../../styles/variables" as *;
|
||||
|
||||
.user-profile-link {
|
||||
a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1.5rem;
|
||||
text-decoration: none;
|
||||
color: $text-dark;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.5px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
// Hover effect
|
||||
&:hover {
|
||||
color: $primary-accent;
|
||||
background: rgba($primary-accent, 0.08);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba($primary-accent, 0.15);
|
||||
}
|
||||
|
||||
// Active state
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 4px rgba($primary-accent, 0.2);
|
||||
}
|
||||
|
||||
// Focus state for accessibility
|
||||
&:focus {
|
||||
outline: 2px solid $primary-accent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
// Subtle animation on hover
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba($primary-accent, 0.1),
|
||||
transparent
|
||||
);
|
||||
transition: left 0.5s ease;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@media (max-width: 768px) {
|
||||
.user-profile-link {
|
||||
a {
|
||||
padding: 0.6rem 1.2rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.user-profile-link {
|
||||
a {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.25px;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserProfileLink } from './user-profile-link';
|
||||
|
||||
describe('UserProfileLink', () => {
|
||||
let component: UserProfileLink;
|
||||
let fixture: ComponentFixture<UserProfileLink>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserProfileLink]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserProfileLink);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'user-profile-link',
|
||||
imports: [],
|
||||
templateUrl: './user-profile-link.html',
|
||||
styleUrl: './user-profile-link.scss'
|
||||
})
|
||||
export class UserProfileLink {
|
||||
|
||||
}
|
16
front/v2/src/app/services/theme/theme.spec.ts
Normal file
16
front/v2/src/app/services/theme/theme.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Theme } from './theme';
|
||||
|
||||
describe('Theme', () => {
|
||||
let service: Theme;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(Theme);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
23
front/v2/src/app/services/theme/theme.ts
Normal file
23
front/v2/src/app/services/theme/theme.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class Theme {
|
||||
private isDarkTheme: boolean = false;
|
||||
|
||||
toggleTheme() {
|
||||
this.isDarkTheme = !this.isDarkTheme;
|
||||
this.applyTheme();
|
||||
}
|
||||
|
||||
isDark() {
|
||||
return this.isDarkTheme;
|
||||
}
|
||||
|
||||
private applyTheme() {
|
||||
const theme = this.isDarkTheme ? 'dark' : 'light';
|
||||
document.body.classList.remove('light-theme', 'dark-theme');
|
||||
document.body.classList.add(`${theme}-theme`);
|
||||
}
|
||||
}
|
16
front/v2/src/app/services/userService/userService.spec.ts
Normal file
16
front/v2/src/app/services/userService/userService.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { userService } from './userService';
|
||||
|
||||
describe('userService', () => {
|
||||
let service: userService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(userService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
20
front/v2/src/app/services/userService/userService.ts
Normal file
20
front/v2/src/app/services/userService/userService.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { userModel } from '../../../models/userModel';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class userService {
|
||||
private currentUser: userModel = userModel.DefaultUser;
|
||||
private userSubject = new BehaviorSubject<userModel>(this.currentUser);
|
||||
|
||||
setUser(user: userModel) {
|
||||
this.currentUser = user;
|
||||
this.userSubject.next(user);
|
||||
}
|
||||
|
||||
getUser() {
|
||||
return this.userSubject.asObservable();
|
||||
}
|
||||
}
|
1
front/v2/src/app/views/admin-view/admin-view.html
Normal file
1
front/v2/src/app/views/admin-view/admin-view.html
Normal file
@@ -0,0 +1 @@
|
||||
<p>admin-view works!</p>
|
0
front/v2/src/app/views/admin-view/admin-view.scss
Normal file
0
front/v2/src/app/views/admin-view/admin-view.scss
Normal file
23
front/v2/src/app/views/admin-view/admin-view.spec.ts
Normal file
23
front/v2/src/app/views/admin-view/admin-view.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminView } from './admin-view';
|
||||
|
||||
describe('AdminView', () => {
|
||||
let component: AdminView;
|
||||
let fixture: ComponentFixture<AdminView>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AdminView]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AdminView);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
11
front/v2/src/app/views/admin-view/admin-view.ts
Normal file
11
front/v2/src/app/views/admin-view/admin-view.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'admin-view',
|
||||
imports: [],
|
||||
templateUrl: './admin-view.html',
|
||||
styleUrl: './admin-view.scss'
|
||||
})
|
||||
export class AdminView {
|
||||
|
||||
}
|
@@ -0,0 +1 @@
|
||||
<p>content-manager-view works!</p>
|
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContentManagerView } from './content-manager-view';
|
||||
|
||||
describe('ContentManagerView', () => {
|
||||
let component: ContentManagerView;
|
||||
let fixture: ComponentFixture<ContentManagerView>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContentManagerView]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ContentManagerView);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'content-manager-view',
|
||||
imports: [],
|
||||
templateUrl: './content-manager-view.html',
|
||||
styleUrl: './content-manager-view.scss'
|
||||
})
|
||||
export class ContentManagerView {
|
||||
|
||||
}
|
1
front/v2/src/app/views/events-view/events-view.html
Normal file
1
front/v2/src/app/views/events-view/events-view.html
Normal file
@@ -0,0 +1 @@
|
||||
<p>events-view works!</p>
|
0
front/v2/src/app/views/events-view/events-view.scss
Normal file
0
front/v2/src/app/views/events-view/events-view.scss
Normal file
23
front/v2/src/app/views/events-view/events-view.spec.ts
Normal file
23
front/v2/src/app/views/events-view/events-view.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EventsView } from './events-view';
|
||||
|
||||
describe('EventsView', () => {
|
||||
let component: EventsView;
|
||||
let fixture: ComponentFixture<EventsView>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [EventsView]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(EventsView);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
11
front/v2/src/app/views/events-view/events-view.ts
Normal file
11
front/v2/src/app/views/events-view/events-view.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'events-view',
|
||||
imports: [],
|
||||
templateUrl: './events-view.html',
|
||||
styleUrl: './events-view.scss'
|
||||
})
|
||||
export class EventsView {
|
||||
|
||||
}
|
7
front/v2/src/app/views/home-view/home-view.html
Normal file
7
front/v2/src/app/views/home-view/home-view.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<ul>
|
||||
@if (gallery.photos) { @for (item of gallery.photos; track $index) {
|
||||
<li>
|
||||
<img [src]="item.lowResUrl" [alt]="item.title" />
|
||||
</li>
|
||||
} }
|
||||
</ul>
|
0
front/v2/src/app/views/home-view/home-view.scss
Normal file
0
front/v2/src/app/views/home-view/home-view.scss
Normal file
23
front/v2/src/app/views/home-view/home-view.spec.ts
Normal file
23
front/v2/src/app/views/home-view/home-view.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HomeView } from './home-view';
|
||||
|
||||
describe('HomeView', () => {
|
||||
let component: HomeView;
|
||||
let fixture: ComponentFixture<HomeView>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [HomeView]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(HomeView);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
14
front/v2/src/app/views/home-view/home-view.ts
Normal file
14
front/v2/src/app/views/home-view/home-view.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { homeGallery } from '../../../models/gallery/homeGallery';
|
||||
|
||||
@Component({
|
||||
selector: 'home-view',
|
||||
imports: [],
|
||||
templateUrl: './home-view.html',
|
||||
styleUrl: './home-view.scss',
|
||||
})
|
||||
export class HomeView implements OnInit {
|
||||
public gallery: homeGallery = new homeGallery();
|
||||
|
||||
ngOnInit() {}
|
||||
}
|
1
front/v2/src/app/views/login-view/login-view.html
Normal file
1
front/v2/src/app/views/login-view/login-view.html
Normal file
@@ -0,0 +1 @@
|
||||
<p>login-view works!</p>
|
0
front/v2/src/app/views/login-view/login-view.scss
Normal file
0
front/v2/src/app/views/login-view/login-view.scss
Normal file
23
front/v2/src/app/views/login-view/login-view.spec.ts
Normal file
23
front/v2/src/app/views/login-view/login-view.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginView } from './login-view';
|
||||
|
||||
describe('LoginView', () => {
|
||||
let component: LoginView;
|
||||
let fixture: ComponentFixture<LoginView>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [LoginView]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LoginView);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
11
front/v2/src/app/views/login-view/login-view.ts
Normal file
11
front/v2/src/app/views/login-view/login-view.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'login-view',
|
||||
imports: [],
|
||||
templateUrl: './login-view.html',
|
||||
styleUrl: './login-view.scss'
|
||||
})
|
||||
export class LoginView {
|
||||
|
||||
}
|
1
front/v2/src/app/views/services-view/services-view.html
Normal file
1
front/v2/src/app/views/services-view/services-view.html
Normal file
@@ -0,0 +1 @@
|
||||
<p>services-view works!</p>
|
23
front/v2/src/app/views/services-view/services-view.spec.ts
Normal file
23
front/v2/src/app/views/services-view/services-view.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ServicesView } from './services-view';
|
||||
|
||||
describe('ServicesView', () => {
|
||||
let component: ServicesView;
|
||||
let fixture: ComponentFixture<ServicesView>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ServicesView]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ServicesView);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
11
front/v2/src/app/views/services-view/services-view.ts
Normal file
11
front/v2/src/app/views/services-view/services-view.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'services-view',
|
||||
imports: [],
|
||||
templateUrl: './services-view.html',
|
||||
styleUrl: './services-view.scss'
|
||||
})
|
||||
export class ServicesView {
|
||||
|
||||
}
|
1
front/v2/src/app/views/tags-view/tags-view.html
Normal file
1
front/v2/src/app/views/tags-view/tags-view.html
Normal file
@@ -0,0 +1 @@
|
||||
<p>tags-view works!</p>
|
0
front/v2/src/app/views/tags-view/tags-view.scss
Normal file
0
front/v2/src/app/views/tags-view/tags-view.scss
Normal file
23
front/v2/src/app/views/tags-view/tags-view.spec.ts
Normal file
23
front/v2/src/app/views/tags-view/tags-view.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TagsView } from './tags-view';
|
||||
|
||||
describe('TagsView', () => {
|
||||
let component: TagsView;
|
||||
let fixture: ComponentFixture<TagsView>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TagsView]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TagsView);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
11
front/v2/src/app/views/tags-view/tags-view.ts
Normal file
11
front/v2/src/app/views/tags-view/tags-view.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'tags-view',
|
||||
imports: [],
|
||||
templateUrl: './tags-view.html',
|
||||
styleUrl: './tags-view.scss'
|
||||
})
|
||||
export class TagsView {
|
||||
|
||||
}
|
@@ -0,0 +1 @@
|
||||
<p>user-galleries-view works!</p>
|
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserGalleriesView } from './user-galleries-view';
|
||||
|
||||
describe('UserGalleriesView', () => {
|
||||
let component: UserGalleriesView;
|
||||
let fixture: ComponentFixture<UserGalleriesView>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserGalleriesView]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserGalleriesView);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'user-galleries-view',
|
||||
imports: [],
|
||||
templateUrl: './user-galleries-view.html',
|
||||
styleUrl: './user-galleries-view.scss'
|
||||
})
|
||||
export class UserGalleriesView {
|
||||
|
||||
}
|
@@ -0,0 +1 @@
|
||||
<p>user-profile-view works!</p>
|
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserProfileView } from './user-profile-view';
|
||||
|
||||
describe('UserProfileView', () => {
|
||||
let component: UserProfileView;
|
||||
let fixture: ComponentFixture<UserProfileView>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserProfileView]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserProfileView);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'user-profile-view',
|
||||
imports: [],
|
||||
templateUrl: './user-profile-view.html',
|
||||
styleUrl: './user-profile-view.scss'
|
||||
})
|
||||
export class UserProfileView {
|
||||
|
||||
}
|
11
front/v2/src/index.html
Normal file
11
front/v2/src/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es" translate="no">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
17
front/v2/src/main.ts
Normal file
17
front/v2/src/main.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { App } from './app/app';
|
||||
import { config } from './models/config';
|
||||
import axios from 'axios';
|
||||
|
||||
async function main() {
|
||||
await config.load();
|
||||
const publicConfig: config = config.get();
|
||||
publicConfig.configure();
|
||||
// update axios base url
|
||||
axios.defaults.baseURL = publicConfig.baseApiUrl || undefined;
|
||||
|
||||
bootstrapApplication(App, appConfig).catch((err) => console.error(err));
|
||||
}
|
||||
|
||||
main();
|
130
front/v2/src/models/config.ts
Normal file
130
front/v2/src/models/config.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class config {
|
||||
private constructor() {} // Evita instanciación
|
||||
|
||||
private static instance: config | null = null;
|
||||
|
||||
private dnsPrefecthBackend() {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'dns-prefetch';
|
||||
link.href = config.instance!.baseApiUrl || 'http://localhost:3000/api';
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
private setIcon() {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'icon';
|
||||
link.type = 'image/x-icon';
|
||||
link.href = config.instance!.faviconSrc || 'default.ico';
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
private setMetaTags() {
|
||||
if (config.instance!.metaTags) {
|
||||
for (const [key, value] of Object.entries(config.instance!.metaTags!)) {
|
||||
const meta = document.createElement('meta');
|
||||
meta.name = key;
|
||||
meta.content = value || '';
|
||||
document.head.appendChild(meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setFonts() {
|
||||
// update font
|
||||
config.instance!.fonts.preconnect.forEach((font) => {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'preconnect';
|
||||
link.href = font.href;
|
||||
if (font.crossorigin) {
|
||||
link.crossOrigin = 'anonymous';
|
||||
}
|
||||
document.head.appendChild(link);
|
||||
});
|
||||
config.instance!.fonts.stylesheets.forEach((stylesheet) => {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = stylesheet;
|
||||
document.head.appendChild(link);
|
||||
});
|
||||
}
|
||||
|
||||
public configure() {
|
||||
document.title = config.instance!.title || 'Default Title';
|
||||
this.dnsPrefecthBackend();
|
||||
this.setIcon();
|
||||
this.setMetaTags();
|
||||
this.setFonts();
|
||||
}
|
||||
|
||||
static get(): config {
|
||||
if (config.instance) {
|
||||
return config.instance!;
|
||||
}
|
||||
throw new Error('Config not loaded. Call config.load() first.');
|
||||
}
|
||||
|
||||
static async load(): Promise<config | null> {
|
||||
if (config.instance === null) {
|
||||
config.instance = new config();
|
||||
const response = await fetch('/config.json');
|
||||
const data = await response.json();
|
||||
config.instance.version = data.version || '1.0.0';
|
||||
config.instance.baseApiUrl =
|
||||
data.baseApiUrl || 'http://localhost:3000/api';
|
||||
config.instance.defaultTheme = data.defaultTheme || 'light';
|
||||
config.instance.logoSrc = data.logoSrc || '/assets/logo.png';
|
||||
config.instance.logoAlt = data.logoAlt || 'Logo';
|
||||
config.instance.faviconSrc = data.faviconSrc || '/assets/favicon.ico';
|
||||
config.instance.title = data.title || 'My Application';
|
||||
config.instance.metaTags = {
|
||||
description: data.metaTags?.description || 'Default description',
|
||||
keywords: data.metaTags?.keywords || 'default,keywords',
|
||||
author: data.metaTags?.author || 'Default Author',
|
||||
};
|
||||
config.instance.socialLinks = {
|
||||
facebook: data.socialLinks?.facebook || null,
|
||||
twitter: data.socialLinks?.twitter || null,
|
||||
instagram: data.socialLinks?.instagram || null,
|
||||
};
|
||||
config.instance.contactEmail = data.contactEmail || null;
|
||||
config.instance.fonts = {
|
||||
preconnect: data.fonts?.preconnect || [],
|
||||
stylesheets: data.fonts?.stylesheets || [],
|
||||
};
|
||||
}
|
||||
return config.instance;
|
||||
}
|
||||
|
||||
public fonts: {
|
||||
preconnect: {
|
||||
href: string;
|
||||
crossorigin?: boolean;
|
||||
type?: string;
|
||||
}[];
|
||||
stylesheets: string[];
|
||||
} = {
|
||||
preconnect: [],
|
||||
stylesheets: [],
|
||||
};
|
||||
public version: string | null = null;
|
||||
public baseApiUrl: string | null = null;
|
||||
public defaultTheme: string | null = null;
|
||||
public logoSrc: string | null = null;
|
||||
public logoAlt: string | null = null;
|
||||
public faviconSrc: string | null = null;
|
||||
public title: string | null = null;
|
||||
public metaTags: {
|
||||
description: string | null;
|
||||
keywords: string | null;
|
||||
author: string | null;
|
||||
} | null = null;
|
||||
public socialLinks: {
|
||||
facebook: string | null;
|
||||
twitter: string | null;
|
||||
instagram: string | null;
|
||||
} | null = null;
|
||||
public contactEmail: string | null = null;
|
||||
}
|
16
front/v2/src/models/eventModel.ts
Normal file
16
front/v2/src/models/eventModel.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { tagModel } from './tagModel';
|
||||
|
||||
export class eventModel {
|
||||
constructor(
|
||||
public id: string,
|
||||
public title: string,
|
||||
public description: string,
|
||||
public date: Date,
|
||||
public location: string,
|
||||
public relatedTags: tagModel[] = [],
|
||||
public createdAt: Date,
|
||||
public updatedAt: Date,
|
||||
public createdBy: string,
|
||||
public updatedBy: string
|
||||
) {}
|
||||
}
|
67
front/v2/src/models/gallery/galleryFactory.ts
Normal file
67
front/v2/src/models/gallery/galleryFactory.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { galleryModel } from './galleryModel';
|
||||
import { homeGallery } from './homeGallery';
|
||||
import { photoModel } from '../photoModel';
|
||||
import { eventModel } from '../eventModel';
|
||||
import { tagModel } from '../tagModel';
|
||||
import { personModel } from '../personModel';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
type galleryArgs = {
|
||||
title?: string | null;
|
||||
description?: string | null;
|
||||
createdBy?: string | null;
|
||||
photos?: photoModel[];
|
||||
isPublic?: boolean;
|
||||
isArchived?: boolean;
|
||||
isFavorite?: boolean;
|
||||
event?: eventModel | null;
|
||||
tags?: tagModel[] | null;
|
||||
personsInvolved?: personModel[] | null;
|
||||
usersWhoCanSee?: personModel[] | null;
|
||||
};
|
||||
|
||||
export class galleryFactory {
|
||||
public static createEmptyGallery(): galleryModel {
|
||||
return this.createGallery({});
|
||||
}
|
||||
|
||||
public static getHomeGallery(): galleryModel {
|
||||
return new homeGallery();
|
||||
}
|
||||
|
||||
public static createGallery(
|
||||
{
|
||||
title = null,
|
||||
description = null,
|
||||
createdBy = null,
|
||||
photos = [],
|
||||
isPublic = true,
|
||||
isArchived = false,
|
||||
isFavorite = false,
|
||||
event = null,
|
||||
tags = null,
|
||||
personsInvolved = null,
|
||||
usersWhoCanSee = null,
|
||||
} = {} as galleryArgs
|
||||
): galleryModel {
|
||||
const now = new Date();
|
||||
|
||||
return new galleryModel({
|
||||
id: uuidv4(),
|
||||
title,
|
||||
description,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
createdBy,
|
||||
updatedBy: createdBy,
|
||||
photos,
|
||||
isPublic,
|
||||
isArchived,
|
||||
isFavorite,
|
||||
event,
|
||||
tags,
|
||||
personsInvolved,
|
||||
usersWhoCanSee,
|
||||
});
|
||||
}
|
||||
}
|
98
front/v2/src/models/gallery/galleryModel.ts
Normal file
98
front/v2/src/models/gallery/galleryModel.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { dataApi } from '../../apiContracts/dataApi';
|
||||
import { paginator } from '../../utils/paginator';
|
||||
import { eventModel } from '../eventModel';
|
||||
import { personModel } from '../personModel';
|
||||
import { photoModel } from '../photoModel';
|
||||
import { tagModel } from '../tagModel';
|
||||
|
||||
type galleryArgs = {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
description?: string | null;
|
||||
createdAt?: Date | null;
|
||||
updatedAt?: Date | null;
|
||||
createdBy?: string | null;
|
||||
updatedBy?: string | null;
|
||||
photos?: photoModel[];
|
||||
isPublic?: boolean;
|
||||
isArchived?: boolean;
|
||||
isFavorite?: boolean;
|
||||
event?: eventModel | null;
|
||||
tags?: tagModel[] | null;
|
||||
personsInvolved?: personModel[] | null;
|
||||
usersWhoCanSee?: personModel[] | null;
|
||||
};
|
||||
|
||||
export class galleryModel {
|
||||
constructor({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
createdBy,
|
||||
updatedBy,
|
||||
photos,
|
||||
isPublic,
|
||||
isArchived,
|
||||
isFavorite,
|
||||
event,
|
||||
tags,
|
||||
personsInvolved,
|
||||
usersWhoCanSee,
|
||||
}: galleryArgs) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
this.createdBy = createdBy;
|
||||
this.updatedBy = updatedBy;
|
||||
this.photos = photos || [];
|
||||
this.isPublic = isPublic;
|
||||
this.isArchived = isArchived;
|
||||
this.isFavorite = isFavorite;
|
||||
this.event = event;
|
||||
this.tags = tags;
|
||||
this.personsInvolved = personsInvolved;
|
||||
this.usersWhoCanSee = usersWhoCanSee;
|
||||
|
||||
this.loadNextPage();
|
||||
}
|
||||
|
||||
public id: string;
|
||||
public title?: string | null = null;
|
||||
public description?: string | null = null;
|
||||
public createdAt?: Date | null = null;
|
||||
public updatedAt?: Date | null = null;
|
||||
public createdBy?: string | null = null;
|
||||
public updatedBy?: string | null = null;
|
||||
public photos: photoModel[] = [];
|
||||
public isPublic?: boolean = true;
|
||||
public isArchived?: boolean = false;
|
||||
public isFavorite?: boolean = false;
|
||||
public event?: eventModel | null = null;
|
||||
public tags?: tagModel[] | null = null;
|
||||
public personsInvolved?: personModel[] | null = null;
|
||||
public usersWhoCanSee?: personModel[] | null = null;
|
||||
|
||||
private paginator = new paginator<photoModel>({
|
||||
url: dataApi.Photos.getAll,
|
||||
});
|
||||
|
||||
public addPhotos(photos: photoModel[]) {
|
||||
if (!this.photos) {
|
||||
this.photos = [];
|
||||
}
|
||||
this.photos.push(...photos);
|
||||
}
|
||||
|
||||
public loadNextPage() {
|
||||
const nextPage = this.paginator.loadNextPage();
|
||||
nextPage.then((photos) => {
|
||||
if (photos) {
|
||||
this.addPhotos(photos);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
12
front/v2/src/models/gallery/homeGallery.ts
Normal file
12
front/v2/src/models/gallery/homeGallery.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { galleryModel } from './galleryModel';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export class homeGallery extends galleryModel {
|
||||
constructor() {
|
||||
super({
|
||||
id: uuidv4(),
|
||||
title: 'Home Gallery',
|
||||
description: 'This is the home gallery.',
|
||||
});
|
||||
}
|
||||
}
|
67
front/v2/src/models/permissionModel.ts
Normal file
67
front/v2/src/models/permissionModel.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
export class permissionModel {
|
||||
constructor(
|
||||
public id: string,
|
||||
public name: string,
|
||||
public description: string
|
||||
) {}
|
||||
|
||||
static ViewContentPermission = new permissionModel(
|
||||
'1',
|
||||
'VIEW_CONTENT',
|
||||
'Permission to view content'
|
||||
);
|
||||
|
||||
static LikeContentPermission = new permissionModel(
|
||||
'2',
|
||||
'LIKE_CONTENT',
|
||||
'Permission to like content'
|
||||
);
|
||||
|
||||
static EditContentPermission = new permissionModel(
|
||||
'3',
|
||||
'EDIT_CONTENT',
|
||||
'Permission to edit content'
|
||||
);
|
||||
|
||||
static DeleteContentPermission = new permissionModel(
|
||||
'4',
|
||||
'DELETE_CONTENT',
|
||||
'Permission to delete content'
|
||||
);
|
||||
|
||||
static CreateContentPermission = new permissionModel(
|
||||
'5',
|
||||
'CREATE_CONTENT',
|
||||
'Permission to create new content'
|
||||
);
|
||||
|
||||
static EditUserPermission = new permissionModel(
|
||||
'6',
|
||||
'EDIT_USER',
|
||||
'Permission to edit user'
|
||||
);
|
||||
|
||||
static DeleteUserPermission = new permissionModel(
|
||||
'7',
|
||||
'DELETE_USER',
|
||||
'Permission to delete user'
|
||||
);
|
||||
|
||||
static DisableUserPermission = new permissionModel(
|
||||
'8',
|
||||
'DISABLE_USER',
|
||||
'Permission to disable user'
|
||||
);
|
||||
|
||||
static CreateUserPermission = new permissionModel(
|
||||
'9',
|
||||
'CREATE_USER',
|
||||
'Permission to create new user'
|
||||
);
|
||||
|
||||
static EditWebConfigPermission = new permissionModel(
|
||||
'10',
|
||||
'EDIT_WEB_CONFIG',
|
||||
'Permission to edit web configuration'
|
||||
);
|
||||
}
|
64
front/v2/src/models/personModel.ts
Normal file
64
front/v2/src/models/personModel.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
type socialMediaType = {
|
||||
facebook: string | null;
|
||||
instagram: string | null;
|
||||
twitter: string | null;
|
||||
blueSky: string | null;
|
||||
tiktok: string | null;
|
||||
linkedin: string | null;
|
||||
pinterest: string | null;
|
||||
discord: string | null;
|
||||
reddit: string | null;
|
||||
other: string | null;
|
||||
};
|
||||
|
||||
type personModelType = {
|
||||
id: string;
|
||||
name: string;
|
||||
profilePicture?: string | null;
|
||||
avatar?: string | null;
|
||||
socialMedia?: socialMediaType | null;
|
||||
};
|
||||
|
||||
export class personModel {
|
||||
constructor({
|
||||
id,
|
||||
name,
|
||||
profilePicture,
|
||||
avatar,
|
||||
socialMedia,
|
||||
}: personModelType) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.profilePicture = profilePicture || null;
|
||||
this.avatar = avatar || null;
|
||||
this.socialMedia = socialMedia || null;
|
||||
}
|
||||
|
||||
public id: string;
|
||||
public name: string;
|
||||
public profilePicture: string | null = null;
|
||||
public avatar: string | null = null;
|
||||
public socialMedia: {
|
||||
facebook: string | null;
|
||||
instagram: string | null;
|
||||
twitter: string | null;
|
||||
blueSky: string | null;
|
||||
tiktok: string | null;
|
||||
linkedin: string | null;
|
||||
pinterest: string | null;
|
||||
discord: string | null;
|
||||
reddit: string | null;
|
||||
other: string | null;
|
||||
} | null = {
|
||||
facebook: null,
|
||||
instagram: null,
|
||||
twitter: null,
|
||||
blueSky: null,
|
||||
tiktok: null,
|
||||
linkedin: null,
|
||||
pinterest: null,
|
||||
discord: null,
|
||||
reddit: null,
|
||||
other: null,
|
||||
};
|
||||
}
|
26
front/v2/src/models/photoModel.ts
Normal file
26
front/v2/src/models/photoModel.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { eventModel } from './eventModel';
|
||||
import { personModel } from './personModel';
|
||||
import { rankingModel } from './rankingModel';
|
||||
import { tagModel } from './tagModel';
|
||||
|
||||
export class photoModel {
|
||||
constructor(
|
||||
public id: string,
|
||||
public title: string,
|
||||
public description: string,
|
||||
public lowResUrl: string,
|
||||
public midResUrl: string,
|
||||
public highResUrl: string,
|
||||
public createdAt: Date,
|
||||
public updatedAt: Date,
|
||||
public createdBy: string,
|
||||
public updatedBy: string,
|
||||
public event: eventModel | null = null,
|
||||
public tags: tagModel[] = [],
|
||||
public ranking: rankingModel = new rankingModel(0),
|
||||
public isFavorite: boolean = false,
|
||||
public isPublic: boolean = true,
|
||||
public isArchived: boolean = false,
|
||||
public persons: personModel[] = []
|
||||
) {}
|
||||
}
|
25
front/v2/src/models/rankingModel.ts
Normal file
25
front/v2/src/models/rankingModel.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export class rankingModel {
|
||||
constructor(
|
||||
private totalVotes: number,
|
||||
private upVotes: number = 0,
|
||||
private downVotes: number = 0
|
||||
) {}
|
||||
|
||||
downVote(): void {
|
||||
this.downVotes++;
|
||||
this.totalVotes++;
|
||||
}
|
||||
|
||||
upVote(): void {
|
||||
this.upVotes++;
|
||||
this.totalVotes++;
|
||||
}
|
||||
|
||||
get score(): number {
|
||||
if (this.totalVotes === 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return (this.upVotes - this.downVotes) / this.totalVotes;
|
||||
}
|
||||
}
|
||||
}
|
63
front/v2/src/models/roleModel.ts
Normal file
63
front/v2/src/models/roleModel.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { permissionModel } from './permissionModel';
|
||||
|
||||
export class roleModel {
|
||||
constructor(
|
||||
public id: string,
|
||||
public name: string,
|
||||
public description: string,
|
||||
public permissions: permissionModel[] = [],
|
||||
public baseRoleModel: roleModel | null = null
|
||||
) {
|
||||
if (baseRoleModel) {
|
||||
this.permissions = baseRoleModel.permissions.concat(this.permissions);
|
||||
}
|
||||
}
|
||||
|
||||
get isAdmin(): boolean {
|
||||
return this.id === roleModel.AdminRole.id;
|
||||
}
|
||||
|
||||
get isContentManager(): boolean {
|
||||
return this.id === roleModel.ContentManagerRole.id;
|
||||
}
|
||||
|
||||
get isUser(): boolean {
|
||||
return this.id === roleModel.UserRole.id;
|
||||
}
|
||||
|
||||
hasPermission(permission: permissionModel): boolean {
|
||||
return this.permissions.some((p) => p.id === permission.id);
|
||||
}
|
||||
|
||||
static UserRole = new roleModel('1', 'User', 'Role for regular users', [
|
||||
permissionModel.ViewContentPermission,
|
||||
permissionModel.LikeContentPermission,
|
||||
]);
|
||||
|
||||
static ContentManagerRole = new roleModel(
|
||||
'2',
|
||||
'Content Manager',
|
||||
'Role for managing content',
|
||||
[
|
||||
permissionModel.CreateUserPermission,
|
||||
permissionModel.DisableUserPermission,
|
||||
permissionModel.CreateContentPermission,
|
||||
permissionModel.EditContentPermission,
|
||||
permissionModel.DeleteContentPermission,
|
||||
],
|
||||
this.UserRole
|
||||
);
|
||||
|
||||
static AdminRole = new roleModel(
|
||||
'3',
|
||||
'Admin',
|
||||
'Administrator role with full permissions',
|
||||
[
|
||||
permissionModel.CreateUserPermission,
|
||||
permissionModel.EditUserPermission,
|
||||
permissionModel.DeleteUserPermission,
|
||||
permissionModel.EditWebConfigPermission,
|
||||
],
|
||||
this.ContentManagerRole
|
||||
);
|
||||
}
|
3
front/v2/src/models/tagModel.ts
Normal file
3
front/v2/src/models/tagModel.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export class tagModel {
|
||||
constructor(public id: string, public name: string) {}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user