diff --git a/.gitignore b/.gitignore index 15c7b211..86c775f8 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ Thumbs.db /src/app/environment /src/app/secret /src/app/app.constant.ts +certificate.cnf +localhost.crt +localhost.key diff --git a/README.md b/README.md index ff9d9436..e22bbf79 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,37 @@ -# UnBTVFrontend +# UnB-TV Frontend -![Alt text](public/Simbolo_UnBTV.svg) +
+logo UNBTV
## Sobre -Este repositório contém o frontend da aplicação UnBTV, um projeto que visa a criação de uma plataforma de streaming para a UnBTV. +O projeto visa o desenvolvimento de uma aplicação Web e Mobile para a UnB-TV, com o objetivo de centralizar e disponibilizar de forma unificada todo o conteúdo oferecido pela UnB-TV, incluindo vídeos e transmissões ao vivo, sendo desenvolvida no segundo semestre de 2023 pelas disciplinas de EPS e MDS da Universidade de Brasília. ## Ambientes [Documentação](https://github.com/fga-eps-mds/2023.2-UnB-TV-DOC) -[Backend:Users](https://github.com/fga-eps-mds/2023.2-UnB-TV-Users) +[Users](https://github.com/fga-eps-mds/2023.2-UnB-TV-Users) +[Admin](https://github.com/fga-eps-mds/2023.2-UnB-TV-Admin) +[Video](https://github.com/fga-eps-mds/2023.2-UnB-TV-VideoService) +[Gateway](https://github.com/fga-eps-mds/2023.2-UnB-TV-API-Gateway) [Frontend](https://github.com/fga-eps-mds/2023.2-UnB-TV-Frontend) +## Acessando o repositório localmente + +### Requisitos + +- docker e docker compose + +Primeiro passo é instalar o docker e docker compose, para isso siga os passos de instalação do [docker](https://docs.docker.com/engine/install/) e [docker compose](https://docs.docker.com/compose/install/). + +Execute o servidor local: + +``` +docker compose up +``` + +Acessar o localhost em: http://localhost:4200 + ## Equipe | Foto | Nome | Github | Email | Matrícula | @@ -30,15 +50,3 @@ Este repositório contém o frontend da aplicação UnBTV, um projeto que visa a | Ricardo de Castro Loureiro | Ricardo de Castro Loureiro | @castroricardo1 | ricardoloureiro75@gmail.com | 200043111 | | Ana Carolina Rodrigues Leite | Sávio Cunha de Carvalho | @savioc2 | saviocunha61@gmail.com | 180130889 | | Vitória Aquere Matos | Vitória Aquere Matos | @vitoriaaquere | <190096616@aluno.unb.br> | 190096616 | - -## Instruções para rodar a aplicação - -Instale o docker e docker compose. - -Para instalar o docker: https://docs.docker.com/engine/install/ -Para instalar o docker compose: https://docs.docker.com/compose/install/ - -Para rodar o frontend execute o comando `docker compose up` e abra no navegador usando o link http://localhost:4200/. - -Para rodar os testes execute `docker exec -it unb-tv-web ng test` com o docker em execução e abra no navegador usando o link http://localhost:9876/# -Para rodar o frontend execute o comando `docker compose up` diff --git a/certificate.cnf b/certificate.cnf new file mode 100644 index 00000000..1986482b --- /dev/null +++ b/certificate.cnf @@ -0,0 +1,21 @@ +[req] +default_bits = 2048 +prompt = no +default_md = sha256 +x509_extensions = v3_req +distinguished_name = dn + +[dn] +C = IN +ST = India +L = India +O = My Organisation +OU = My Organisational Unit +emailAddress = email@domain.com +CN = localhost + +[v3_req] +subjectAltName = @alt_names + +[alt_names] +DNS.1 = localhost \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 21edd442..9a090329 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "unb-tv-frontend", "version": "0.0.0", "dependencies": { + "@abacritt/angularx-social-login": "^2.0.0", "@angular/animations": "^15.2.0", "@angular/cdk": "^15.2.9", "@angular/common": "^15.2.0", @@ -18,9 +19,11 @@ "@angular/platform-browser": "^15.2.0", "@angular/platform-browser-dynamic": "^15.2.0", "@angular/router": "^15.2.0", + "@types/gapi.auth2": "^0.0.60", "angular-oauth2-oidc": "^15.0.1", "hls.js": "^1.4.12", "jwt-decode": "^3.1.2", + "ngx-google-analytics": "^14.0.1", "postcss-cli": "^10.1.0", "postcss-import": "^15.1.0", "postcss-loader": "^7.3.3", @@ -49,6 +52,18 @@ "typescript": "~4.9.4" } }, + "node_modules/@abacritt/angularx-social-login": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@abacritt/angularx-social-login/-/angularx-social-login-2.0.0.tgz", + "integrity": "sha512-SgbkgOTlcbaOmGDL6eCMIXct4+nFpzWjYqUfN7gqyBdb7D/rlo0hDd0hntMqoK5Tc3AA1z2ZAg+fZlSaBuRvEg==", + "dependencies": { + "tslib": ">=2.5.0" + }, + "peerDependencies": { + "@angular/common": ">=15.0.0", + "@angular/core": ">=15.0.0" + } + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -5260,6 +5275,19 @@ "@types/send": "*" } }, + "node_modules/@types/gapi": { + "version": "0.0.47", + "resolved": "https://registry.npmjs.org/@types/gapi/-/gapi-0.0.47.tgz", + "integrity": "sha512-/ZsLuq6BffMgbKMtZyDZ8vwQvTyKhKQ1G2K6VyWCgtHHhfSSXbk4+4JwImZiTjWNXfI2q1ZStAwFFHSkNoTkHA==" + }, + "node_modules/@types/gapi.auth2": { + "version": "0.0.60", + "resolved": "https://registry.npmjs.org/@types/gapi.auth2/-/gapi.auth2-0.0.60.tgz", + "integrity": "sha512-KQiyTHRyAcat6marTRAixrkHhkS7mqclcbQvInfxyp7IeUDSUzLBlgH1s0SFaLl1m1jRY6b+qh4NMlmVm0wPEw==", + "dependencies": { + "@types/gapi": "*" + } + }, "node_modules/@types/http-errors": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", @@ -10274,6 +10302,18 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/ngx-google-analytics": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/ngx-google-analytics/-/ngx-google-analytics-14.0.1.tgz", + "integrity": "sha512-PfOtnshSyq15EKevKlFW9IRgH+dTtPG4Q9HJYksuRNYDzjce0eqK3Bf6hz0tAZdyqbzTCyx5g+NgWBfpqQfb2w==", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@angular/common": ">=12.0.0", + "@angular/core": ">=12.0.0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", diff --git a/package.json b/package.json index 2fe0aef2..0e6b2fef 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ }, "private": true, "dependencies": { + "@abacritt/angularx-social-login": "^2.0.0", "@angular/animations": "^15.2.0", "@angular/cdk": "^15.2.9", "@angular/common": "^15.2.0", @@ -23,6 +24,7 @@ "@angular/platform-browser": "^15.2.0", "@angular/platform-browser-dynamic": "^15.2.0", "@angular/router": "^15.2.0", + "@types/gapi.auth2": "^0.0.60", "angular-oauth2-oidc": "^15.0.1", "hls.js": "^1.4.12", "jwt-decode": "^3.1.2", diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 7352b34e..ebc2e5d5 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -16,41 +16,76 @@ import { UpdateRoleComponent } from './pages/update-role/update-role.component'; import { AdminGuard } from './services/admin.guard'; import { SuggestAgendaComponent } from './pages/suggest-agenda/suggest-agenda.component'; import { ParticipateComponent } from './pages/participate/participate.component'; -import { GridDaysComponent } from './pages/grid-days/grid-days.component'; -import { GridComponent } from './pages/grid/grid.component'; +import { GridDaysComponent } from './pages/grid-days/grid-days.component'; +import { GridComponent } from './pages/grid/grid.component'; +import { CatalogComponent } from './pages/catalog/catalog.component'; import { WithTokenGuard } from './guard/with-token.guard'; const routes: Routes = [ - { path: 'login', component: LoginComponent }, - { path: 'register', component: RegisterComponent }, - { path: 'loginsocial', component: LoginSocialComponent }, { path: 'stream', component: StreamViewComponent }, + { path: 'catalog', component: CatalogComponent }, { path: 'videos', component: VideoComponent }, { path: 'video/:idVideo', component: VideoViewerComponent }, - { path: 'activeAccount', component: ActiveAccountComponent }, - { path: 'sendCodeResetPassword', component: CheckCodeRestPasswordComponent }, - { path: 'changePassword', component: ResetPasswordComponent }, - { path: '', component: VideoComponent, canActivate: [AuthGuard], }, // Default route - Showd be stream component - { path: 'login', component: LoginComponent, canActivate: [WithTokenGuard], }, - { path: 'register', component: RegisterComponent, canActivate: [WithTokenGuard], }, - { path: 'loginsocial', component: LoginSocialComponent, canActivate: [WithTokenGuard], }, - { path: 'sendCodeResetPassword', component: CheckCodeRestPasswordComponent, canActivate: [WithTokenGuard], }, - { path: 'changePassword', component: ResetPasswordComponent, canActivate: [WithTokenGuard], }, - { path: 'videos', component: VideoComponent, canActivate: [AuthGuard], }, - { path: 'video/:idVideo', component: VideoViewerComponent, canActivate: [AuthGuard], }, - { path: 'activeAccount', component: ActiveAccountComponent, canActivate: [WithTokenGuard], }, - { path: 'suggestAgenda', component: SuggestAgendaComponent, canActivate: [AuthGuard], }, - { path: 'participate', component: ParticipateComponent, canActivate: [AuthGuard], }, - { path: 'profile', component: ProfileComponent, canActivate: [AuthGuard], }, - { path: 'editUser/:id', component: EditUserComponent, canActivate: [AuthGuard], }, + { path: '', component: CatalogComponent }, + { path: 'login', component: LoginComponent, canActivate: [WithTokenGuard] }, + { + path: 'register', + component: RegisterComponent, + canActivate: [WithTokenGuard], + }, + { + path: 'loginsocial', + component: LoginSocialComponent, + canActivate: [WithTokenGuard], + }, + { + path: 'sendCodeResetPassword', + component: CheckCodeRestPasswordComponent, + canActivate: [WithTokenGuard], + }, + { + path: 'changePassword', + component: ResetPasswordComponent, + canActivate: [WithTokenGuard], + }, + { + path: 'video/:idVideo', + component: VideoViewerComponent, + canActivate: [AuthGuard], + }, + { + path: 'activeAccount', + component: ActiveAccountComponent, + canActivate: [WithTokenGuard], + }, + { + path: 'suggestAgenda', + component: SuggestAgendaComponent, + canActivate: [AuthGuard], + }, + { + path: 'participate', + component: ParticipateComponent, + canActivate: [AuthGuard], + }, + { path: 'profile', component: ProfileComponent, canActivate: [AuthGuard] }, + { + path: 'editUser/:id', + component: EditUserComponent, + canActivate: [AuthGuard], + }, { path: 'grid-days', component: GridDaysComponent }, { path: 'grid-days/:day', component: GridComponent }, - { path: 'update-role', component: UpdateRoleComponent, canActivate: [AdminGuard], } + { + path: 'update-role', + component: UpdateRoleComponent, + canActivate: [AdminGuard], + }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule], }) -export class AppRoutingModule { } +export class AppRoutingModule {} diff --git a/src/app/app.constant.ts b/src/app/app.constant.ts index ed684343..dad406ae 100644 --- a/src/app/app.constant.ts +++ b/src/app/app.constant.ts @@ -1,2 +1,5 @@ export const EDUPLAY_API_URL = 'https://eduplay.rnp.br/services/'; -export const UNB_ID = 216; \ No newline at end of file +export const UNB_ID = 216; +export const VIDEOS_LIMIT = 1000; +export const VIDEOS_ORDER = 3; // ordenação pelos mais recentes (eduplay); +export const UNB_TV_CHANNEL_ID = 190265; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4dd1beb2..51af47d2 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -11,6 +11,9 @@ import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; import { InputTextModule } from 'primeng/inputtext'; import { DropdownModule } from 'primeng/dropdown'; import { ButtonModule } from 'primeng/button'; +import { SocialLoginModule, SocialAuthServiceConfig } from '@abacritt/angularx-social-login'; +import { GoogleLoginProvider, FacebookLoginProvider } from '@abacritt/angularx-social-login'; + // Declaration import { NgModule } from '@angular/core'; @@ -40,6 +43,7 @@ import { GridComponent } from './pages/grid/grid.component'; import { GridDaysComponent } from './pages/grid-days/grid-days.component'; import { ProgressSpinnerModule } from 'primeng/progressspinner'; import { NgxGoogleAnalyticsModule, NgxGoogleAnalyticsRouterModule } from 'ngx-google-analytics'; +import { CatalogComponent } from './pages/catalog/catalog.component'; @NgModule({ imports: [ @@ -58,6 +62,7 @@ import { NgxGoogleAnalyticsModule, NgxGoogleAnalyticsRouterModule } from 'ngx-go FormsModule, BrowserAnimationsModule, MatPaginatorModule, + SocialLoginModule, NgxGoogleAnalyticsModule.forRoot('G-XL7Z0L7VM8'), NgxGoogleAnalyticsRouterModule ], @@ -81,7 +86,8 @@ import { NgxGoogleAnalyticsModule, NgxGoogleAnalyticsRouterModule } from 'ngx-go ParticipateComponent, GridComponent, GridDaysComponent, - VideoCommentComponent + VideoCommentComponent, + CatalogComponent ], providers: [ @@ -94,7 +100,28 @@ import { NgxGoogleAnalyticsModule, NgxGoogleAnalyticsRouterModule } from 'ngx-go }, { provide: OAuthStorage, useValue: localStorage }, MessageService, - ConfirmationService + ConfirmationService, + { + provide: 'SocialAuthServiceConfig', + useValue: { + autoLogin: false, + providers: [ + { + id: GoogleLoginProvider.PROVIDER_ID, + provider: new GoogleLoginProvider( + '254484469180-1imr4ds36p8rq4fe7udkja212tu0p7jl.apps.googleusercontent.com' + ) + }, + { + id: FacebookLoginProvider.PROVIDER_ID, + provider: new FacebookLoginProvider('2640880742734858') + } + ], + onError: (err) => { + console.error(err); + } + } as SocialAuthServiceConfig, + } ], bootstrap: [AppComponent], }) diff --git a/src/app/components/background/background.component.html b/src/app/components/background/background.component.html index fa449f34..52121646 100644 --- a/src/app/components/background/background.component.html +++ b/src/app/components/background/background.component.html @@ -3,63 +3,67 @@
simbolo-unb
-
-
@@ -70,7 +74,9 @@ >
{ let guard: AuthGuard; + let router: Router; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( - [{ path: 'login', component: LoginComponent }] - )], providers: [AuthGuard], declarations: [LoginComponent] + [{ path: 'loginsocial', component: LoginComponent }] + )], + providers: [AuthGuard], + declarations: [LoginComponent] }); guard = TestBed.inject(AuthGuard); + router = TestBed.inject(Router); }); it('should be created', () => { @@ -23,13 +28,12 @@ describe('AuthGuard', () => { it('should return true for a logged in user', () => { localStorage.setItem('token', 'testtoken'); expect(guard.canActivate()).toBe(true); - } - ); + }); - it('should return false for a logged out user', () => { + it('should navigate to loginsocial for a logged out user', () => { localStorage.removeItem('token'); - expect(guard.canActivate()).toBe(false); - } - ); - + const navigateSpy = spyOn(router, 'navigate'); + guard.canActivate(); + expect(navigateSpy).toHaveBeenCalledWith(['/loginsocial']); + }); }); diff --git a/src/app/guard/auth.guard.ts b/src/app/guard/auth.guard.ts index f98f0c30..ed5df328 100644 --- a/src/app/guard/auth.guard.ts +++ b/src/app/guard/auth.guard.ts @@ -13,7 +13,7 @@ export class AuthGuard implements CanActivate { if (this.authService.isAuthenticated()) { return true; } else { - this.router.navigate(['/login']); + this.router.navigate(['/loginsocial']); return false; } } diff --git a/src/app/guard/with-token.guard.spec.ts b/src/app/guard/with-token.guard.spec.ts index e49f3ec7..2e7648ee 100644 --- a/src/app/guard/with-token.guard.spec.ts +++ b/src/app/guard/with-token.guard.spec.ts @@ -10,9 +10,14 @@ describe('WithTokenGuard', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule.withRoutes( - [{ path: 'videos', component: VideoComponent }] - )], providers: [WithTokenGuard], declarations: [VideoComponent] + imports: [ + HttpClientTestingModule, + RouterTestingModule.withRoutes([ + { path: 'catalog', component: VideoComponent }, + ]), + ], + providers: [WithTokenGuard], + declarations: [VideoComponent], }); guard = TestBed.inject(WithTokenGuard); }); @@ -24,12 +29,10 @@ describe('WithTokenGuard', () => { it('should return false for a logged in user', () => { localStorage.setItem('token', 'testtoken'); expect(guard.canActivate()).toBe(false); - } - ); + }); it('should return true for a logged out user', () => { localStorage.removeItem('token'); expect(guard.canActivate()).toBe(true); - } - ); + }); }); diff --git a/src/app/guard/with-token.guard.ts b/src/app/guard/with-token.guard.ts index b296b0d9..63b8c939 100644 --- a/src/app/guard/with-token.guard.ts +++ b/src/app/guard/with-token.guard.ts @@ -3,7 +3,7 @@ import { CanActivate, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class WithTokenGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) {} @@ -12,7 +12,7 @@ export class WithTokenGuard implements CanActivate { if (!this.authService.isAuthenticated()) { return true; } else { - this.router.navigate(['/videos']); + this.router.navigate(['/catalog']); return false; } } diff --git a/src/app/pages/active-account/active-account.component.spec.ts b/src/app/pages/active-account/active-account.component.spec.ts index c65b875b..51fc6f48 100644 --- a/src/app/pages/active-account/active-account.component.spec.ts +++ b/src/app/pages/active-account/active-account.component.spec.ts @@ -157,4 +157,11 @@ describe('ActiveAccountComponent', () => { expect(myspy).toHaveBeenCalled(); }); + it('should resend code on clicking "Reenviar Código"', () => { + spyOn(component, 'resendCode'); + component.resendCode(); + expect(component.resendCode).toHaveBeenCalled(); + +}); + }); diff --git a/src/app/pages/catalog/catalog.component.css b/src/app/pages/catalog/catalog.component.css new file mode 100644 index 00000000..a30100ef --- /dev/null +++ b/src/app/pages/catalog/catalog.component.css @@ -0,0 +1,7 @@ +.center { + margin: 0 auto; +} + +.thumbnail:hover { + box-shadow: 0 0 8px rgba(0, 0, 0, 0.3); +} diff --git a/src/app/pages/catalog/catalog.component.html b/src/app/pages/catalog/catalog.component.html new file mode 100644 index 00000000..31c0dd24 --- /dev/null +++ b/src/app/pages/catalog/catalog.component.html @@ -0,0 +1,333 @@ +
+
+

Jornalismo

+
+
+ capa do programa 'Fala, jovem' +
+
+ capa do programa 'Informe UnB' +
+
+ capa do programa 'Zapping' +
+
+
+ +
+

Entrevista

+
+
+ capa do programa 'Brasil em Questão' +
+
+ capa do programa 'Diálogos' +
+
+ capa do programa 'Tirando de letra' +
+
+ capa do programa 'UnBTV Entrevista' +
+
+ capa do programa 'Vasto Mundo' +
+
+ capa do programa 'Vozes Diplomáticas' +
+
+
+ +
+

+ Pesquisa e Ciência +

+
+
+ capa do programa 'Explique sua Tese' +
+
+ capa do programa 'Fazendo Ciência Formando Cientistas' +
+
+ capa do programa 'Radar da Extensão' +
+
+ capa do programa 'Se Liga no PAS' +
+
+ capa do programa 'UnBTV Ciência' +
+
+ capa do programa 'Universidade Para Quê?' +
+
+
+ +
+

+ Arte e Cultura +

+
+
+ capa do programa 'Em Cantos' +
+
+ capa do programa 'Casa do Som' +
+
+ capa do programa 'Esboços' +
+
+ capa do programa 'Exclusiva' +
+
+
+ +
+

+ Séries Especiais +

+
+
+ capa do programa 'Floresta de Gente' +
+
+ capa do programa 'Guia do Calouro' +
+
+ capa do programa 'Memorias de Paulo Freire' +
+
+ capa do programa 'Os desafios das eleicoes 2022' +
+
+ capa do programa 'Podcast Vida de Estudante' +
+
+ capa do programa 'Serie Arquitetura' +
+
+
+ +
+

+ Documentais +

+
+
+ capa do programa 'Mini Doc' +
+
+ capa do programa 'Documentários' +
+
+
+ +
+

Variedades

+
+
+ capa do programa 'Pitadas do Cerrado' +
+
+
+
diff --git a/src/app/pages/catalog/catalog.component.spec.ts b/src/app/pages/catalog/catalog.component.spec.ts new file mode 100644 index 00000000..74420337 --- /dev/null +++ b/src/app/pages/catalog/catalog.component.spec.ts @@ -0,0 +1,775 @@ +import { Router } from '@angular/router'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of, throwError } from 'rxjs'; + +import { CatalogComponent } from './catalog.component'; +import { VideoService } from 'src/app/services/video.service'; +import { IVideo } from 'src/shared/model/video.model'; + +const mockData = { + qtTotal: 110, + videoList: [ + { + id: 142471, + title: 'Sala de Reunião 04 do NTE', + description: 'Sala de Reunião 04 do NTE', + keywords: 'Sala de Reunião 04 do NTE', + visibility: 'PUBLIC', + duration: 561835, + generateLibras: true, + generateSubtitle: true, + qtAccess: 41, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1630091029232.png', + }, + ], + userOwner: { + id: 30684, + name: 'Fabio Ferreira de Oliveira', + }, + }, + { + id: 180741, + title: 'Mulheres que inspiram - Profa. Carla Rocha', + description: + '

Entrevista com a Professora Carla Rocha (UnB) como parte do projeto Mulheres que Inspiram. Adeia surgiu através da leitura do texto Eu programo, tu programas, elx hackea: mulheres hackers e perspectivas tecnopolíticas e do interesse em dar visibilidade a mulheres que atuam na área de software livre e educação aberta. Transcrição do áudio disponível..em https://pt.wikiversity.org/wiki/Educa%C3%A7%C3%A3o_Aberta/Mulheres_que_inspiram


Disponível com uma licença CC-BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/).

', + keywords: + 'mulheres, computação, professora, ciência da computação, gênero', + visibility: 'PUBLIC', + duration: 508459, + generateLibras: true, + generateSubtitle: true, + qtAccess: 36, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1679168690066.jpg', + }, + ], + userOwner: { + id: 44590, + name: 'Tel Amiel', + avatar: + 'https://eduplay.rnp.br/portal/assets/users/images/1683107832084.jpg', + }, + }, + { + id: 184760, + title: + 'Dia 4: Minicurso de Extensão - UnB - Racontez-nous votre histoire', + keywords: 'mconf', + visibility: 'PUBLIC', + duration: 5161003, + generateLibras: true, + generateSubtitle: true, + qtAccess: 24, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1687455434581.png', + }, + ], + userOwner: { + id: 45799, + name: 'Denise Gisele de Britto Damasco', + }, + }, + { + id: 184518, + title: 'Dia 1: Minicurso de Extensão - UnB - Présentation Denise Damasco', + keywords: 'mconf', + visibility: 'PUBLIC', + duration: 3532366, + generateLibras: true, + generateSubtitle: true, + qtAccess: 24, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1686872273578.png', + }, + ], + userOwner: { + id: 45799, + name: 'Denise Gisele de Britto Damasco', + }, + }, + { + id: 111840, + title: 'Aulas Síncronas ás 14 horas as terças-feiras e quintas-feiras', + description: + 'Aulas Síncronas ás 14 horas as terças-feiras e quintas-feiras', + keywords: 'Aulas Síncronas ás 14 horas as terças-feiras e quintas-feiras', + visibility: 'PUBLIC', + duration: 6111573, + generateLibras: true, + generateSubtitle: true, + qtAccess: 38, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1621608406505.png', + }, + ], + userOwner: { + id: 28703, + name: 'Jorlandio Francisco Felix', + }, + }, + { + id: 141981, + title: 'Liliane Campos Machado', + description: 'Liliane Campos Machado', + keywords: 'Liliane Campos Machado', + visibility: 'PUBLIC', + duration: 6482260, + generateLibras: true, + generateSubtitle: true, + qtAccess: 162, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1629828306049.png', + }, + ], + userOwner: { + id: 29754, + name: 'Liliane Campos Machado', + }, + }, + { + id: 111535, + title: 'NTE videoconferências', + description: 'NTE videoconferências', + keywords: 'NTE videoconferências', + visibility: 'PUBLIC', + duration: 7508000, + generateLibras: true, + generateSubtitle: true, + qtAccess: 73, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1621383436732.png', + }, + ], + userOwner: { + id: 19994, + name: 'Endryl Francelino de Souza', + }, + }, + { + id: 184759, + title: + 'Dia 3: Minicurso de Extensão - UnB - Racontez-nous votre histoire', + keywords: 'mconf', + visibility: 'PUBLIC', + duration: 5175560, + generateLibras: true, + generateSubtitle: true, + qtAccess: 24, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1687454416354.png', + }, + ], + userOwner: { + id: 45799, + name: 'Denise Gisele de Britto Damasco', + }, + }, + { + id: 179217, + title: 'Patricia Tuxi dos Santos', + description: 'Patricia Tuxi dos Santos', + keywords: 'Patricia Tuxi dos Santos', + visibility: 'PUBLIC', + duration: 109163, + generateLibras: true, + generateSubtitle: true, + qtAccess: 42, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1675876887636.png', + }, + ], + userOwner: { + id: 44725, + name: 'Patricia Tuxi dos Santos', + }, + }, + { + id: 142458, + title: 'Sala de Reunião 04 do NTE', + description: 'Sala de Reunião 04 do NTE', + keywords: 'Sala de Reunião 04 do NTE', + visibility: 'PUBLIC', + duration: 2750269, + generateLibras: true, + generateSubtitle: true, + qtAccess: 75, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1630088527294.png', + }, + ], + userOwner: { + id: 30684, + name: 'Fabio Ferreira de Oliveira', + }, + }, + ], +}; + +const mockVideoData: IVideo[] = [ + { + id: 190985, + title: 'Esboços: Luiz Gallina', + description: + '


A curiosidade de Luiz Gallina em descobrir e entender como mecanismos e processos funcionam dá origem a uma obra composta por gravuras, pinturas e esculturas - abordando temas tão diversos quanto as árvores da paisagem de Brasília, a alquimia e os sonhos colecionados de forma escrita pelo artista durante anos.



', + keywords: 'Esboços; Luiz Gallina; UnBTV;', + visibility: 'PUBLIC', + duration: 1527827, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 20, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1699445118833.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190984, + title: 'Esboços: Ricardo Caldeira', + description: + '


Quando Ricardo Caldeira desenha parece dançar imprimindo na tela um traço marcante que fala sobre afeto, negritude, ancestralidade. Aqui o artista relembra sua infância e adolescência, os primeiros trabalhos e a relação com a região de São Sebastião, local que o influenciou e que hoje ele transforma por meio da participação em coletivos culturais.



', + keywords: 'Esboços; Ricardo Caldeira; UnBTV;', + visibility: 'PUBLIC', + duration: 1525621, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 14, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1699445004278.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190334, + title: 'Esboços | Fernanda Pacca', + description: + '


Confetes, braçadeiras de náilon, botões, linhas de costuras compõe a palheta de cores que constroem surpreendentes figuras humanas na obra de Fernanda Pacca. Trabalhos que impressionam pela forma e abordam diversos temas como o fascínio pelo anatomia, a repressão - tanto estética quanto social - e a violência contra a mulher. 





', + keywords: 'Fernanda Pacca; Esboços; unbtv;', + visibility: 'PUBLIC', + duration: 1550174, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 15, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1698243181940.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190333, + title: 'Esboços | Valéria Pena-Costa', + description: + '


Poeira, insetos, objetos em deterioração são, ao mesmo tempo, tema e matéria-prima para a obra de Valéria Pena-Costa em um trabalho que reflete a inevitável passagem do tempo, as memórias de infância, os medos passados e presentes em diferentes formas de expressão.


', + keywords: 'Valéria Pena-Costa; Esboços; unbtv;', + visibility: 'PUBLIC', + duration: 1547193, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 23, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1698243362095.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190324, + title: 'Esboços: TNHA', + description: + '


A busca por uma identidade que integre raízes negras, indígenas e latino-americanas cria uma obra que resgata ancestralidade, de olho sempre nas injustiças históricas. Seja espalhando rostos em grafites pelo Distrito Federal para suavizar o caminho de todo dia; seja tatuando ou fazendo ilustrações, TNHA tem um traço singular que carrega todo um aprendizado de andanças por diferentes comunidades e vivências familiares. 


', + keywords: 'esboços; Tnha; unbtv;', + visibility: 'PUBLIC', + duration: 1550383, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 14, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1698146507222.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, +]; + +class VideoServiceMock { + findAll() { + return of(mockData); + } + setVideosCatalog(videos: IVideo[]) {} +} + +describe('CatalogComponent', () => { + let component: CatalogComponent; + let fixture: ComponentFixture; + let videoService: VideoService; + let router: Router; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CatalogComponent], + imports: [HttpClientTestingModule], + providers: [{ provide: VideoService, useValue: new VideoServiceMock() }], + }).compileComponents(); + + fixture = TestBed.createComponent(CatalogComponent); + component = fixture.componentInstance; + videoService = TestBed.inject(VideoService); + router = TestBed.inject(Router); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('shoud call findAll', () => { + spyOn(component, 'findAll'); + fixture.detectChanges(); + expect(component.findAll).toHaveBeenCalled(); + }); + + it('should call findAll and return a list of videos', () => { + const mySpy = spyOn(videoService, 'findAll').and.callThrough(); + component.findAll(); + expect(mySpy).toHaveBeenCalled(); + }); + + it('should call findAll and return an error', () => { + const mySpy = spyOn(videoService, 'findAll').and.returnValue( + throwError(() => new Error('Erro')) + ); + component.findAll(); + expect(mySpy).toHaveBeenCalled(); + }); + + it('should filter videos by channel', () => { + const videos: IVideo[] = [ + { + id: 192504, + title: 'Exclusiva | Banda Aparte', + description: + '


No Exclusiva, recebemos a banda brasiliense Aparte. Eles falaram sobra a sua criação, sobre os desafios de ser uma banda independente e trouxeram algumas lembranças da época da faculdade. Confira!



', + keywords: 'Entrevista; Exclusiva; Banda Aparte; UnB; UnBTV;', + visibility: 'PUBLIC', + duration: 1009627, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 4, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1702032708404.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 191441, + title: + 'Cálculo 1 - Definição de Integral | Integrais Def. e Indef. | T.F.C | Regra da Potência e da Soma', + description: '


', + keywords: 'mconf', + visibility: 'PUBLIC', + duration: 4719080, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 25, + qtLikes: 1, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1700532539037.png', + }, + ], + channels: [ + { + id: 191486, + name: 'Minicurso de Integrais - Universidade de Brasília', + }, + ], + }, + ]; + + const filteredVideosByChannel: IVideo[] = [ + { + id: 192504, + title: 'Exclusiva | Banda Aparte', + description: + '


No Exclusiva, recebemos a banda brasiliense Aparte. Eles falaram sobra a sua criação, sobre os desafios de ser uma banda independente e trouxeram algumas lembranças da época da faculdade. Confira!



', + keywords: 'Entrevista; Exclusiva; Banda Aparte; UnB; UnBTV;', + visibility: 'PUBLIC', + duration: 1009627, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 4, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1702032708404.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + ]; + component.filterVideosByChannel(videos); + expect(component.unbTvVideos).toEqual(filteredVideosByChannel); + }); + + it('should organize videos into categories', () => { + const videos: IVideo[] = [ + { + id: 1, + title: 'Fala, Jovem Video', + keywords: 'fala, jovem', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 2, + title: 'Informe UnB Video', + keywords: 'informe unb', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 3, + title: 'Zapping Video', + keywords: 'zapping', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 4, + title: 'Brasil em Questão Video', + keywords: 'brasil em questão', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 5, + title: 'Diálogos Video', + keywords: 'diálogos', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 6, + title: 'Tirando de Letra Video', + keywords: 'tirando de letra', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 7, + title: 'Entrevista Video', + keywords: 'entrevista', + channels: [{ id: 1, name: 'UnBTV' }], + }, + { + id: 8, + title: 'Vasto mundo Video', + keywords: 'vasto mundo', + channels: [{ id: 1, name: 'UnBTV' }], + }, + { + id: 9, + title: 'Vozes Diplomáticas Video', + keywords: 'vozes diplomáticas', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 10, + title: 'Explique sua Tese Video', + keywords: 'explique sua tese', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 11, + title: 'Fazendo Ciência Video', + keywords: 'fazendo ciência', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 12, + title: 'Radar da Extensão Video', + keywords: 'radar da extensão', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 13, + title: 'se liga no pas Video', + keywords: 'se liga no pas', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 14, + title: 'UnbTV Ciência Video', + keywords: 'unbtv ciência', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 15, + title: 'Universidade para quê? Video', + keywords: 'universidade para quê?', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 16, + title: '[Em]Cantos Video', + keywords: '[em]cantos', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 17, + title: 'Casa do Som Video', + keywords: 'casa do som', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 18, + title: 'Esboços Video', + keywords: 'esboços', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 18, + title: 'Exclusiva Video', + keywords: 'exclusiva', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 19, + title: 'floresta de gente Video', + keywords: 'floresta de gente', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 20, + title: 'guia do calouro Video', + keywords: 'guia do calouro', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 21, + title: 'Memórias sobre Paulo Freire Video', + keywords: 'memórias sobre paulo freire', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 22, + title: 'Desafios das Eleições Video', + keywords: 'desafios das eleições', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 23, + title: 'vida de estudante Video', + keywords: 'vida de estudante', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 24, + title: 'Sobre Arquitetura Video', + keywords: 'arquitetura', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 25, + title: 'Mini Doc Video', + keywords: 'mini doc', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 26, + title: 'Documentário sobre testes Video', + keywords: 'documentário', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 27, + title: 'Pitadas do cerrado Video', + keywords: 'pitadas do cerrado', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + { + id: 50, + title: 'Sem keywords', + keywords: 'sem keywords', + channels: [{ id: 190265, name: 'UnBTV' }], + }, + ]; + + component.videosCatalog(videos); + + expect(component.catalog.journalism.falaJovem.length).toBeGreaterThan(0); + expect(component.catalog.journalism.informeUnB.length).toBeGreaterThan(0); + expect(component.catalog.journalism.zapping.length).toBeGreaterThan(0); + expect(component.catalog.interviews.brasilEmQuestao.length).toBeGreaterThan( + 0 + ); + expect(component.catalog.interviews.dialogos.length).toBeGreaterThan(0); + expect(component.catalog.interviews.tirandoDeLetra.length).toBeGreaterThan( + 0 + ); + expect(component.catalog.interviews.entrevistas.length).toBeGreaterThan(0); + expect(component.catalog.interviews.vastoMundo.length).toBeGreaterThan(0); + expect( + component.catalog.interviews.vozesDiplomaticas.length + ).toBeGreaterThan(0); + expect( + component.catalog.researchAndScience.expliqueSuaTese.length + ).toBeGreaterThan(0); + expect( + component.catalog.researchAndScience.fazendoCiencia.length + ).toBeGreaterThan(0); + expect( + component.catalog.researchAndScience.fazendoCiencia.length + ).toBeGreaterThan(0); + expect( + component.catalog.researchAndScience.radarDaExtencao.length + ).toBeGreaterThan(0); + expect( + component.catalog.researchAndScience.seLigaNoPAS.length + ).toBeGreaterThan(0); + expect( + component.catalog.researchAndScience.universidadeParaQue.length + ).toBeGreaterThan(0); + expect(component.catalog.artAndCulture.emCantos.length).toBeGreaterThan(0); + expect(component.catalog.artAndCulture.casaDoSom.length).toBeGreaterThan(0); + expect(component.catalog.artAndCulture.esbocos.length).toBeGreaterThan(0); + expect(component.catalog.artAndCulture.exclusiva.length).toBeGreaterThan(0); + expect( + component.catalog.specialSeries.florestaDeGente.length + ).toBeGreaterThan(0); + expect( + component.catalog.specialSeries.guiaDoCalouro.length + ).toBeGreaterThan(0); + expect( + component.catalog.specialSeries.memoriasPauloFreire.length + ).toBeGreaterThan(0); + expect( + component.catalog.specialSeries.desafiosDasEleicoes.length + ).toBeGreaterThan(0); + expect( + component.catalog.specialSeries.vidaDeEstudante.length + ).toBeGreaterThan(0); + expect( + component.catalog.specialSeries.arquiteturaICC.length + ).toBeGreaterThan(0); + expect(component.catalog.documentaries.miniDoc.length).toBeGreaterThan(0); + expect( + component.catalog.documentaries.documentaries.length + ).toBeGreaterThan(0); + expect(component.catalog.varieties.pitadasDoCerrado.length).toBeGreaterThan( + 0 + ); + expect(component.catalog.unbtv.length).toBeGreaterThan(0); + }); + + it('should navigate to "/videos" when onProgramClick is called', () => { + const videos: IVideo[] = mockVideoData; + + const videosCatalogSpy = spyOn(videoService, 'setVideosCatalog'); + const navigateSpy = spyOn(router, 'navigate'); + + component.onProgramClick(videos); + + expect(videosCatalogSpy).toHaveBeenCalledWith(videos); + expect(navigateSpy).toHaveBeenCalledWith(['/videos']); + }); +}); diff --git a/src/app/pages/catalog/catalog.component.ts b/src/app/pages/catalog/catalog.component.ts new file mode 100644 index 00000000..a32187d7 --- /dev/null +++ b/src/app/pages/catalog/catalog.component.ts @@ -0,0 +1,191 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { UNB_TV_CHANNEL_ID } from 'src/app/app.constant'; +import { VideoService } from 'src/app/services/video.service'; +import { Catalog } from 'src/shared/model/catalog.model'; +import { IVideo } from 'src/shared/model/video.model'; + +@Component({ + selector: 'app-catalog', + templateUrl: './catalog.component.html', + styleUrls: ['./catalog.component.css'], +}) +export class CatalogComponent { + unbTvChannelId = UNB_TV_CHANNEL_ID; + videosEduplay: IVideo[] = []; + unbTvVideos: IVideo[] = []; + catalog: Catalog = new Catalog(); + + constructor(private videoService: VideoService, private router: Router) {} + + ngOnInit(): void { + this.findAll(); + } + + findAll(): void { + this.videoService.findAll().subscribe({ + next: (data) => { + this.videosEduplay = data.body?.videoList ?? []; + }, + error: (error) => { + console.log(error); + }, + complete: () => { + this.filterVideosByChannel(this.videosEduplay); + this.videosCatalog(this.unbTvVideos); + }, + }); + } + + filterVideosByChannel(videos: IVideo[]): void { + videos.forEach((video) => { + const channel = video?.channels; + + if (channel) + if (channel[0].id === this.unbTvChannelId) this.unbTvVideos.push(video); + }); + } + + videosCatalog(videos: IVideo[]): void { + const keywordsCategories = [ + { + keywords: ['fala, jovem'], + category: this.catalog.journalism.falaJovem, + }, + { + keywords: ['informe unb'], + category: this.catalog.journalism.informeUnB, + }, + { keywords: ['zapping'], category: this.catalog.journalism.zapping }, + { + keywords: ['brasil em questão'], + category: this.catalog.interviews.brasilEmQuestao, + }, + { keywords: ['diálogos'], category: this.catalog.interviews.dialogos }, + { + keywords: ['tirando de letra'], + category: this.catalog.interviews.tirandoDeLetra, + }, + { + keywords: ['entrevista'], + category: this.catalog.interviews.entrevistas, + }, + { + keywords: ['vasto mundo'], + category: this.catalog.interviews.vastoMundo, + }, + { + keywords: ['vozes diplomáticas'], + category: this.catalog.interviews.vozesDiplomaticas, + }, + { + keywords: ['explique sua tese'], + category: this.catalog.researchAndScience.expliqueSuaTese, + }, + { + keywords: ['fazendo ciência'], + category: this.catalog.researchAndScience.fazendoCiencia, + }, + { + keywords: ['radar da extensão'], + category: this.catalog.researchAndScience.radarDaExtencao, + }, + { + keywords: ['se liga no pas'], + category: this.catalog.researchAndScience.seLigaNoPAS, + }, + { + keywords: ['unbtv ciência'], + category: this.catalog.researchAndScience.unbTvCiencia, + }, + { + keywords: ['universidade pra quê?', 'universidade para quê?'], + category: this.catalog.researchAndScience.universidadeParaQue, + }, + { + keywords: ['[em]cantos'], + category: this.catalog.artAndCulture.emCantos, + }, + { + keywords: ['casa do som'], + category: this.catalog.artAndCulture.casaDoSom, + }, + { keywords: ['esboços'], category: this.catalog.artAndCulture.esbocos }, + { + keywords: ['exclusiva'], + category: this.catalog.artAndCulture.exclusiva, + }, + { + keywords: ['floresta de gente'], + category: this.catalog.specialSeries.florestaDeGente, + }, + { + keywords: ['guia do calouro'], + category: this.catalog.specialSeries.guiaDoCalouro, + }, + { + keywords: ['memórias sobre paulo freire'], + category: this.catalog.specialSeries.memoriasPauloFreire, + }, + { + keywords: ['desafios das eleições'], + category: this.catalog.specialSeries.desafiosDasEleicoes, + }, + { + keywords: ['vida de estudante'], + category: this.catalog.specialSeries.vidaDeEstudante, + }, + { + keywords: ['arquitetura'], + category: this.catalog.specialSeries.arquiteturaICC, + }, + { + keywords: [ + 'mini doc', + 'cerrado de volta', + 'construção tradicional kalunga', + 'o muro', + 'um lugar para onde voltar', + 'vidas no cárcere', + ], + category: this.catalog.documentaries.miniDoc, + }, + { + keywords: [ + 'documentários', + 'documentário', + 'quanto vale um terço?', + 'refazendo os caminhos de george gardner', + 'sem hora para chegar', + 'todas podem ser vitímas', + ], + category: this.catalog.documentaries.documentaries, + }, + { + keywords: ['pitadas do cerrado'], + category: this.catalog.varieties.pitadasDoCerrado, + }, + ]; + + videos.forEach((video) => { + const keywordsTitle = video?.title?.toLowerCase() ?? ''; + + if (keywordsTitle) { + const category = keywordsCategories.find((config) => + config.keywords.some((keyword) => keywordsTitle.includes(keyword)) + ); + + if (category) { + category.category.push(video); + } else { + this.catalog.unbtv.push(video); + } + } + }); + } + + onProgramClick(videos: IVideo[]) { + this.videoService.setVideosCatalog(videos); + this.router.navigate(['/videos']); + } +} diff --git a/src/app/pages/edit-user/edit-user.component.ts b/src/app/pages/edit-user/edit-user.component.ts index f96db7a5..2767e5f3 100644 --- a/src/app/pages/edit-user/edit-user.component.ts +++ b/src/app/pages/edit-user/edit-user.component.ts @@ -98,7 +98,7 @@ export class EditUserComponent implements OnInit { 'Sucesso', 'Usuário atualizado com sucesso!' ); - this.navigator('/profile'); + this.navigator('/videos'); }, error: (error: ErrorResponseType) => { this.alertService.errorMessage(error.error); diff --git a/src/app/pages/grid/grid.component.css b/src/app/pages/grid/grid.component.css index 2facea38..2d8fa603 100644 --- a/src/app/pages/grid/grid.component.css +++ b/src/app/pages/grid/grid.component.css @@ -2,22 +2,36 @@ padding-left: 40px; } .padded-cell-day { - padding-left: 20px; + padding-left: 10px; + } +.padded-right { + padding-right: 20px; } -.center-table { - font-size: 12px; -} table { width: 100%; border-collapse: collapse; + font-size: 13px; } -th, td { - padding: 8px; + padding: 5px; } + tr:not(:last-child) { border-bottom-width: 0.01em; +} + +td::first-letter { + text-transform: capitalize; +} +td { + text-transform: lowercase; + +} + +p { + font-size: 15px; + } \ No newline at end of file diff --git a/src/app/pages/grid/grid.component.html b/src/app/pages/grid/grid.component.html index 5134f215..26d352e8 100644 --- a/src/app/pages/grid/grid.component.html +++ b/src/app/pages/grid/grid.component.html @@ -10,15 +10,18 @@
- - - - - + + + + + + + + - - + +
Programação diária
HorárioAtividade
HorárioAtividade
{{ item.time }}{{ item.activity }}{{ item.time }}{{ item.activity }}
diff --git a/src/app/pages/login-social/login-social.component.css b/src/app/pages/login-social/login-social.component.css index 807ed9e3..6c365106 100644 --- a/src/app/pages/login-social/login-social.component.css +++ b/src/app/pages/login-social/login-social.component.css @@ -1,13 +1,20 @@ - +@font-face { + font-family: "Helvetic"; + src: url("../../../assets/fonts/Helvetica.ttf") format("truetype"); + font-weight: normal; + font-style: normal; +} .container { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - margin-left: 80px; - height: 100%; - margin-top: -70px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + max-width: 360px; + margin-right: auto; + margin-left: auto; + height: 100%; } .google-button { @@ -21,13 +28,12 @@ top: 210px; left: 50px; margin: 5px; - border: 0.15px solid #3C3C3B; + border: 0.15px solid #3C3C3B; border-radius: 9px; line-height: 10px; font-weight: 400; font-size: 13px; background-image: url('../../../assets/google.png'); - background-position: center; background-size: 7% auto; background-repeat: no-repeat; background-position: 220px 6px; @@ -59,18 +65,20 @@ font-weight: 400; font-size: 13px; background-image: url('../../../assets/facebook.png'); - background-position: center; background-size: 8% auto; background-repeat: no-repeat; background-position: 220px 4px; margin-bottom: 10.5px; margin-top: 10.5px; - + } -.facebook-button span{ - position: relative; - margin-left: -55px; +.facebook-button span { + position: relative; + margin-left: 30px; + font-family: Helvetic, sans-serif; + font-size: 14px; + color: white; } .email-button{ @@ -84,7 +92,7 @@ top: 210px; left: 50px; margin: 5px; - border: 0.15px solid #3C3C3B; + border: 0.15px solid #3C3C3B; border-radius: 9px; line-height: 10px; font-weight: 400; @@ -94,15 +102,15 @@ } -.email-button span{ - position: relative; - margin-left: -85px; +.email-button span { + position: relative; + margin-left: -5px; + font-size: 14px; } .cadastro { - font-size: 13px; - color: #0087C8; - margin-left: -140px; - margin-top: 18.5px + font-size: 13px; + color: #0087c8; + margin-left: -72px; + margin-top: 18.5px; } - diff --git a/src/app/pages/login-social/login-social.component.html b/src/app/pages/login-social/login-social.component.html index 682d5909..3c028444 100644 --- a/src/app/pages/login-social/login-social.component.html +++ b/src/app/pages/login-social/login-social.component.html @@ -1,10 +1,22 @@
+ diff --git a/src/app/pages/login-social/login-social.component.spec.ts b/src/app/pages/login-social/login-social.component.spec.ts index 9d476b73..029a45be 100644 --- a/src/app/pages/login-social/login-social.component.spec.ts +++ b/src/app/pages/login-social/login-social.component.spec.ts @@ -1,17 +1,47 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { LoginSocialComponent } from './login-social.component'; +import { SocialAuthService, SocialUser, FacebookLoginProvider } from '@abacritt/angularx-social-login'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { AuthService } from '../../services/auth.service'; +import { of } from 'rxjs'; +import { Component } from '@angular/core'; + +@Component({ template: '' }) +class DummyComponent {} describe('LoginSocialComponent', () => { let component: LoginSocialComponent; let fixture: ComponentFixture; + let mockSocialAuthService: jasmine.SpyObj; + let mockAuthService: jasmine.SpyObj; beforeEach(async () => { + mockSocialAuthService = jasmine.createSpyObj('SocialAuthService', ['signIn', 'signOut']); + mockAuthService = jasmine.createSpyObj('AuthService', ['loginSocialUser']); + mockAuthService.loginSocialUser.and.returnValue(of({ + access_token: 'mock-token', + is_new_user: false, + user_id: 'mock-user-id' + })); + await TestBed.configureTestingModule({ - declarations: [ LoginSocialComponent ] - }) - .compileComponents(); + declarations: [ LoginSocialComponent, DummyComponent ], + imports: [ + HttpClientTestingModule, + RouterTestingModule.withRoutes([ + { path: 'videos', component: DummyComponent }, + { path: 'editUser/:id', component: DummyComponent } + ]) + ], + providers: [ + { provide: SocialAuthService, useValue: mockSocialAuthService }, + { provide: AuthService, useValue: mockAuthService }, + ] + }).compileComponents(); + }); + beforeEach(() => { fixture = TestBed.createComponent(LoginSocialComponent); component = fixture.componentInstance; fixture.detectChanges(); @@ -20,4 +50,41 @@ describe('LoginSocialComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call signIn when signing in with Facebook', async () => { + const user = new SocialUser(); + user.provider = 'FACEBOOK'; + user.id = '1234567890'; + user.name = 'Test User'; + user.email = 'test@example.com'; + + mockSocialAuthService.signIn.and.returnValue(Promise.resolve(user)); + await component.signInWithFB(); + + expect(mockSocialAuthService.signIn).toHaveBeenCalledWith(FacebookLoginProvider.PROVIDER_ID); + }); + + it('should process JWT response correctly', () => { + const payload = { name: 'Test User', email: 'test@example.com' }; + const encodedPayload = btoa(JSON.stringify(payload)); + const testResponse = { + credential: `header.${encodedPayload}.signature` + }; + + component.handleCredentialResponse(testResponse); + }); + + it('should handle Facebook login and send user data to server', async () => { + const user = new SocialUser(); + user.provider = 'FACEBOOK'; + user.id = '1234567890'; + user.name = 'Test User'; + user.email = 'test@example.com'; + + mockSocialAuthService.signIn.and.returnValue(Promise.resolve(user)); + await component.signInWithFB(); + + expect(mockSocialAuthService.signIn).toHaveBeenCalledWith(FacebookLoginProvider.PROVIDER_ID); + expect(mockAuthService.loginSocialUser).toHaveBeenCalledWith({ name: user.name, email: user.email }); + }); }); diff --git a/src/app/pages/login-social/login-social.component.ts b/src/app/pages/login-social/login-social.component.ts index d8e6d1a7..fa0853aa 100644 --- a/src/app/pages/login-social/login-social.component.ts +++ b/src/app/pages/login-social/login-social.component.ts @@ -1,10 +1,115 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { SocialAuthService, SocialUser } from '@abacritt/angularx-social-login'; +import { FacebookLoginProvider } from '@abacritt/angularx-social-login'; +import { HttpClient } from '@angular/common/http'; +import { Router } from '@angular/router'; +import { AuthService } from '../../services/auth.service'; +declare var gapi: any; + +interface ServerResponse { + access_token: string; + is_new_user: boolean; + user_id: string; +} + +interface GoogleIdentityResponse { + credential: string; +} + +interface GoogleIdentityResponse { + credential: string; + clientId: string; +} + +declare global { + interface Window { + handleCredentialResponse: (response: any) => void; + } +} + @Component({ selector: 'app-login-social', templateUrl: './login-social.component.html', styleUrls: ['./login-social.component.css'] }) -export class LoginSocialComponent { +export class LoginSocialComponent implements OnInit { + private user: SocialUser | null = null; + private loggedIn: boolean = false; + + + constructor( + private authService: SocialAuthService, + private http: HttpClient, + private router: Router, + private Service: AuthService + ) { } + + + + ngOnInit(): void { + window['handleCredentialResponse'] = this.handleCredentialResponse.bind(this); + this.loadGoogleButton(); + } + + loadGoogleButton(): void { + const googleScript = document.createElement('script'); + googleScript.src = 'https://accounts.google.com/gsi/client'; + googleScript.async = true; + googleScript.defer = true; + document.body.appendChild(googleScript); + } + + handleCredentialResponse(response: any) { + const jwt = response.credential; + const payload = jwt.split('.')[1]; + const decodedPayload = atob(payload); + const userInformation = JSON.parse(decodedPayload); + + const user: SocialUser = new SocialUser(); + user.name = userInformation.name; + user.email = userInformation.email; + + this.sendUserDataToServer(user); + } + + + signInWithFB(): void { + this.authService.signIn(FacebookLoginProvider.PROVIDER_ID).then((user) => { + + if (user) { + this.sendUserDataToServer(user); + } + }); + } + + signOut(): void { + this.authService.signOut(); + } + private sendUserDataToServer(user: SocialUser) { + const userSocialData = { + name: user.name, + email: user.email + }; + + this.Service.loginSocialUser(userSocialData).subscribe( + (response) => { + console.log('Resposta do servidor:', response); + + if (response && response.access_token) { + localStorage.setItem('token', response.access_token); + + if (response.is_new_user) { + this.router.navigate([`/editUser/${response.user_id}`]); + } else { + this.router.navigate(['/videos']); + } + } + }, + (error) => { + console.error('Erro ao enviar dados para o servidor:', error); + } + ); + } } diff --git a/src/app/pages/login/login.component.html b/src/app/pages/login/login.component.html index 32b83ed3..095d7c02 100644 --- a/src/app/pages/login/login.component.html +++ b/src/app/pages/login/login.component.html @@ -1,5 +1,5 @@ -
+
@@ -19,5 +19,8 @@
Cadastre-se
+
+ Login com redes sociais +
diff --git a/src/app/pages/login/login.component.spec.ts b/src/app/pages/login/login.component.spec.ts index c890b38a..aaa6ee93 100644 --- a/src/app/pages/login/login.component.spec.ts +++ b/src/app/pages/login/login.component.spec.ts @@ -1,4 +1,9 @@ -import { ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing'; +import { + ComponentFixture, + TestBed, + tick, + fakeAsync, +} from '@angular/core/testing'; import { LoginComponent } from './login.component'; import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { HttpClientTestingModule } from '@angular/common/http/testing'; @@ -13,14 +18,14 @@ import { VideoComponent } from '../video/video.component'; import { HttpErrorResponse } from '@angular/common/http'; import { ActiveAccountComponent } from '../active-account/active-account.component'; - const mockUserReturn = { - "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJqb2FvMTV2aWN0b3IwOEBnbWFpbC5jb20iLCJleHAiOjE2OTkzMTI5MzV9.1B9qBJt8rErwBKyD5JCdsPozsw86oQ38tdfDuMM2HFI", - "token_type": "bearer" + access_token: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJqb2FvMTV2aWN0b3IwOEBnbWFpbC5jb20iLCJleHAiOjE2OTkzMTI5MzV9.1B9qBJt8rErwBKyD5JCdsPozsw86oQ38tdfDuMM2HFI', + token_type: 'bearer', }; class AuthServiceMock { - constructor() { } + constructor() {} loginUser() { return of({ success: true }); @@ -28,7 +33,7 @@ class AuthServiceMock { } class AlertServiceMock { - constructor() { } + constructor() {} showMessage() { return of({ success: true }); } @@ -45,21 +50,26 @@ describe('LoginComponent', () => { beforeEach(async () => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule.withRoutes( - [ + imports: [ + HttpClientTestingModule, + ReactiveFormsModule, + RouterTestingModule.withRoutes([ { path: 'profile', component: ProfileComponent }, - { path: 'sendCodeResetPassword', component: CheckCodeRestPasswordComponent }, + { + path: 'sendCodeResetPassword', + component: CheckCodeRestPasswordComponent, + }, { path: 'register', component: RegisterComponent }, { path: 'videos', component: VideoComponent }, - { path: 'activeAccount', component: ActiveAccountComponent } - ] - )], + { path: 'activeAccount', component: ActiveAccountComponent }, + ]), + ], providers: [ FormBuilder, AuthService, AlertService, { provide: AlertService, useValue: new AlertServiceMock() }, // Provide the mock class - { provide: AuthService, useValue: new AuthServiceMock() } + { provide: AuthService, useValue: new AuthServiceMock() }, ], declarations: [LoginComponent], }).compileComponents(); @@ -85,7 +95,9 @@ describe('LoginComponent', () => { const form = component.userForm; form.setValue({ email: 'test@example.com', password: 'password' }); - const submitButton = fixture.nativeElement.querySelector('button[type="submit"]'); + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); submitButton.click(); tick(); @@ -97,16 +109,23 @@ describe('LoginComponent', () => { const alertSpy = spyOn(alertService, 'showMessage'); fixture.detectChanges(); - const submitButton = fixture.nativeElement.querySelector('button[type="submit"]'); + const submitButton = fixture.nativeElement.querySelector( + 'button[type="submit"]' + ); submitButton.click(); tick(); - expect(alertSpy).toHaveBeenCalledWith('info', 'Alerta', 'Preencha todos os campos corretamente!'); + expect(alertSpy).toHaveBeenCalledWith( + 'info', + 'Alerta', + 'Preencha todos os campos corretamente!' + ); })); it('should call navigator method when "Esqueceu a senha?" is clicked', () => { spyOn(component, 'navigator').and.callThrough(); - const forgotPasswordLink = fixture.nativeElement.querySelector('.text-gray-400'); + const forgotPasswordLink = + fixture.nativeElement.querySelector('.text-gray-400'); forgotPasswordLink.click(); expect(component.navigator).toHaveBeenCalledWith('/sendCodeResetPassword'); @@ -114,21 +133,36 @@ describe('LoginComponent', () => { it('should call navigator method when "Cadastre-se" is clicked', () => { spyOn(component, 'navigator').and.callThrough(); - const registerLink = fixture.nativeElement.querySelector('.text-blue-brand'); + const registerLink = + fixture.nativeElement.querySelector('.text-blue-brand'); registerLink.click(); expect(component.navigator).toHaveBeenCalledWith('/register'); }); + it('should call navigator method when "Login com redes sociais" is clicked', () => { + const navigatorSpy = spyOn(component, 'navigator').and.callThrough(); + const loginSocial = fixture.nativeElement.querySelector('#loginSocialButton'); + loginSocial.click(); + + expect(navigatorSpy).toHaveBeenCalledWith('/loginsocial'); + }); + it('should call login and return an error', () => { fixture.detectChanges(); const form = component.userForm; form.setValue({ email: 'test@example.com', password: 'password' }); - const mySpy = spyOn(authService, 'loginUser').and.returnValue(throwError(() => new HttpErrorResponse({ error: { detail: 'A sua conta ainda não foi ativada.' } }))); + const mySpy = spyOn(authService, 'loginUser').and.returnValue( + throwError( + () => + new HttpErrorResponse({ + error: { detail: 'A sua conta ainda não foi ativada.' }, + }) + ) + ); spyOn(component, 'navigator').and.callThrough(); component.login(); expect(mySpy).toHaveBeenCalled(); expect(component.navigator).toHaveBeenCalledWith('/activeAccount'); }); - }); diff --git a/src/app/pages/profile/profile.component.html b/src/app/pages/profile/profile.component.html index 27ea7fd5..5b04933b 100644 --- a/src/app/pages/profile/profile.component.html +++ b/src/app/pages/profile/profile.component.html @@ -8,10 +8,10 @@
- + {{ user.name }} - + {{ user.email }}
diff --git a/src/app/pages/profile/profile.component.spec.ts b/src/app/pages/profile/profile.component.spec.ts index 8f879141..38def172 100644 --- a/src/app/pages/profile/profile.component.spec.ts +++ b/src/app/pages/profile/profile.component.spec.ts @@ -38,6 +38,8 @@ class AlertServiceMock { class AuthServiceMock { logout() { } + refreshToken() { } + showRenewTokenDialog() { } } class ConfirmationServiceMock { @@ -125,6 +127,49 @@ describe('ProfileComponent', () => { expect(mySpy).toHaveBeenCalled(); }); + it('should call refreshToken and set token in localStorage', () => { + spyOn(authService, 'refreshToken').and.returnValue(of({ access_token: 'new_access_token' })); + + component.renewToken(); + + expect(authService.refreshToken).toHaveBeenCalled(); + expect(localStorage.getItem('token')).toEqual('new_access_token'); + }); + + it('should handle error when renewing token', () => { + spyOn(authService, 'refreshToken').and.returnValue(throwError('error')); + spyOn(console, 'error'); + + component.renewToken(); + + expect(authService.refreshToken).toHaveBeenCalled(); + expect(console.error).toHaveBeenCalledWith('Failed to refresh token:', 'error'); + }); + it('should show renew token dialog', () => { + const confirmSpy = spyOn(confirmationService, 'confirm').and.callFake((params: any) => { + params.accept(); + params.reject(); + + // Return a mock ConfirmationService instance + return {} as ConfirmationService; + }); + const renewTokenSpy = spyOn(component, 'renewToken'); + const logoutSpy = spyOn(authService, 'logout'); + + component.showRenewTokenDialog(); + + expect(confirmSpy).toHaveBeenCalledWith({ + message: 'Deseja se manter logado?', + header: 'Confirmação', + key: 'myDialog', + icon: 'pi pi-exclamation-triangle', + accept: jasmine.any(Function), + reject: jasmine.any(Function), + }); + expect(renewTokenSpy).toHaveBeenCalled(); + expect(logoutSpy).toHaveBeenCalled(); + }); + it('should call navigatorEdit when editUser is clicked', () => { spyOn(component, 'navigatorEdit').and.callThrough(); const navigateSpy = spyOn(router, 'navigate'); diff --git a/src/app/pages/profile/profile.component.ts b/src/app/pages/profile/profile.component.ts index f147e0a5..520a564e 100644 --- a/src/app/pages/profile/profile.component.ts +++ b/src/app/pages/profile/profile.component.ts @@ -7,6 +7,7 @@ import { AlertService } from 'src/app/services/alert.service'; import { ConfirmationService, MessageService } from 'primeng/api'; import { AuthService } from 'src/app/services/auth.service'; import { HttpErrorResponse } from '@angular/common/http'; +import { take, timer } from 'rxjs'; type ErrorResponseType = HttpErrorResponse; @@ -32,6 +33,11 @@ export class ProfileComponent { ngOnInit(): void { this.setUserIdFromToken(localStorage.getItem('token') as string); this.getUser(); + timer(15 * 60 * 1000) + .pipe(take(1)) + .subscribe(() => { + this.showRenewTokenDialog(); + }); } setUserIdFromToken(token: string) { @@ -91,6 +97,35 @@ export class ProfileComponent { }); } + showRenewTokenDialog() { + this.confirmationService.confirm({ + message: 'Deseja se manter logado?', + header: 'Confirmação', + key: 'myDialog', + icon: 'pi pi-exclamation-triangle', + accept: () => { + this.renewToken(); + }, + reject: () => { + this.authService.logout(); + }, + }); + } + + renewToken() { + this.authService.refreshToken().subscribe({ + next: (response) => { + if (response?.access_token) { + localStorage.setItem('token', response.access_token); + } + }, + error: (error: ErrorResponseType) => { + console.error('Failed to refresh token:', error); + this.authService.logout(); + } + }); + } + navigatorEdit(): void { this.router.navigate([`/editUser/${this.user.id}`]); } diff --git a/src/app/pages/suggest-agenda/suggest-agenda.component.css b/src/app/pages/suggest-agenda/suggest-agenda.component.css index 0c29b52e..7add1ccc 100644 --- a/src/app/pages/suggest-agenda/suggest-agenda.component.css +++ b/src/app/pages/suggest-agenda/suggest-agenda.component.css @@ -1,10 +1,9 @@ .containner { - width: 360px; + max-width: 360px; margin-left: auto; margin-right: auto; text-align: center; - height: -300px; } .containner h1 { @@ -23,11 +22,8 @@ .first { border: 0.15px solid #969696; - margin-left: 29px; - width: 330px; border-radius: 9px; - height: 30px; - padding: 9px 0 10px 28px; + padding: 8px 0 8px 24px; } form ::placeholder { @@ -37,11 +33,9 @@ form ::placeholder { .descricao { border: 0.15px solid #969696; - margin-left: 29px; - width: 330px; border-radius: 9px; height: 100px; - padding: 9px 0 10px 28px; + padding: 8px 0 8px 24px; } .descricao::placeholder { @@ -50,7 +44,6 @@ form ::placeholder { button { background-color: #0087C8; - padding: 3px 33px 3px 33px; border-radius: 9px; margin-top: 7px; } @@ -67,7 +60,6 @@ span { } .button { - padding: 3px 33px 3px 33px; border-radius: 9px; margin-top: 7px; } diff --git a/src/app/pages/suggest-agenda/suggest-agenda.component.html b/src/app/pages/suggest-agenda/suggest-agenda.component.html index 85ac454e..bcbbb0b8 100644 --- a/src/app/pages/suggest-agenda/suggest-agenda.component.html +++ b/src/app/pages/suggest-agenda/suggest-agenda.component.html @@ -1,22 +1,77 @@ -
-
-

Sugestão de Pautas

-
- - - - - - - -
-

*Campo obrigatório

+
+
+

Sugestão de Pautas

+ + + + + + + + +

+ *Campo obrigatório +

- -
- -
+ + +
diff --git a/src/app/pages/suggest-agenda/suggest-agenda.component.spec.ts b/src/app/pages/suggest-agenda/suggest-agenda.component.spec.ts index 744534e2..6f29c732 100644 --- a/src/app/pages/suggest-agenda/suggest-agenda.component.spec.ts +++ b/src/app/pages/suggest-agenda/suggest-agenda.component.spec.ts @@ -75,7 +75,7 @@ describe('SuggestAgendaComponent', () => { fixture.detectChanges(); const mySpy = spyOn(emailService, 'sendEmail').and.callThrough(); const form = component.suggestAgendaForm; - form.setValue({ descricao: 'Descrição', responsavel: 'Usuário Teste', telefoneResponsavel: '999999999', tema: '', quando: '', local: '', emailContato: '' }) + form.setValue({ descricao: 'Descrição', responsavel: 'Usuário Teste', telefoneResponsavel: '(99) 99999-9999', tema: '', quando: '', local: '', emailContato: 'test@example.xxx' }) component.sendSuggestAgenda(); expect(mySpy).toHaveBeenCalled(); }); @@ -84,7 +84,7 @@ describe('SuggestAgendaComponent', () => { fixture.detectChanges(); const mySpy = spyOn(emailService, 'sendEmail').and.returnValue(throwError(() => new Error('Erro'))); const form = component.suggestAgendaForm; - form.setValue({ descricao: 'Descrição', responsavel: 'Usuário Teste', telefoneResponsavel: '999999999', tema: '', quando: '', local: '', emailContato: '' }) + form.setValue({ descricao: 'Descrição', responsavel: 'Usuário Teste', telefoneResponsavel: '(99) 99999-9999', tema: '', quando: '', local: '', emailContato: '' }) component.sendSuggestAgenda(); expect(mySpy).toHaveBeenCalled(); }); diff --git a/src/app/pages/suggest-agenda/suggest-agenda.component.ts b/src/app/pages/suggest-agenda/suggest-agenda.component.ts index bf5bdcbe..eac79c69 100644 --- a/src/app/pages/suggest-agenda/suggest-agenda.component.ts +++ b/src/app/pages/suggest-agenda/suggest-agenda.component.ts @@ -29,7 +29,7 @@ export class SuggestAgendaComponent implements OnInit { local: [''], responsavel: ['', [Validators.required]], telefoneResponsavel: ['', [Validators.required]], - emailContato: [''], + emailContato: ['', [Validators.pattern('^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$')]], }, ); } @@ -60,5 +60,4 @@ export class SuggestAgendaComponent implements OnInit { this.alertService.showMessage("info", "Alerta", "Preencha todos os campos corretamente!"); } } - } diff --git a/src/app/pages/video-viewer/video-viewer.component.html b/src/app/pages/video-viewer/video-viewer.component.html index b53d2ed6..00eb5295 100644 --- a/src/app/pages/video-viewer/video-viewer.component.html +++ b/src/app/pages/video-viewer/video-viewer.component.html @@ -1,4 +1,7 @@
+
+ Share +
', + generateLibras: true, + generateSubtitle: true, + qtAccess: 20, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1699445118833.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190984, + title: 'Esboços: Ricardo Caldeira', + description: + '


Quando Ricardo Caldeira desenha parece dançar imprimindo na tela um traço marcante que fala sobre afeto, negritude, ancestralidade. Aqui o artista relembra sua infância e adolescência, os primeiros trabalhos e a relação com a região de São Sebastião, local que o influenciou e que hoje ele transforma por meio da participação em coletivos culturais.



', + keywords: 'Esboços; Ricardo Caldeira; UnBTV;', + visibility: 'PUBLIC', + duration: 1525621, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 14, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1699445004278.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190334, + title: 'Esboços | Fernanda Pacca', + description: + '


Confetes, braçadeiras de náilon, botões, linhas de costuras compõe a palheta de cores que constroem surpreendentes figuras humanas na obra de Fernanda Pacca. Trabalhos que impressionam pela forma e abordam diversos temas como o fascínio pelo anatomia, a repressão - tanto estética quanto social - e a violência contra a mulher. 





', + keywords: 'Fernanda Pacca; Esboços; unbtv;', + visibility: 'PUBLIC', + duration: 1550174, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 15, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1698243181940.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190333, + title: 'Esboços | Valéria Pena-Costa', + description: + '


Poeira, insetos, objetos em deterioração são, ao mesmo tempo, tema e matéria-prima para a obra de Valéria Pena-Costa em um trabalho que reflete a inevitável passagem do tempo, as memórias de infância, os medos passados e presentes em diferentes formas de expressão.


', + keywords: 'Valéria Pena-Costa; Esboços; unbtv;', + visibility: 'PUBLIC', + duration: 1547193, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 23, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1698243362095.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190324, + title: 'Esboços: TNHA', + description: + '


A busca por uma identidade que integre raízes negras, indígenas e latino-americanas cria uma obra que resgata ancestralidade, de olho sempre nas injustiças históricas. Seja espalhando rostos em grafites pelo Distrito Federal para suavizar o caminho de todo dia; seja tatuando ou fazendo ilustrações, TNHA tem um traço singular que carrega todo um aprendizado de andanças por diferentes comunidades e vivências familiares. 


', + keywords: 'esboços; Tnha; unbtv;', + visibility: 'PUBLIC', + duration: 1550383, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 14, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1698146507222.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, +]; class VideoServiceMock { - constructor() { } - findAll() { + getVideosCatalog() { return of(mockData); } } @@ -239,39 +155,50 @@ describe('VideoComponent', () => { let component: VideoComponent; let fixture: ComponentFixture; let videoService: VideoService; + let router: Router; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [VideoComponent], imports: [HttpClientTestingModule], - providers: [{ provide: VideoService, useValue: new VideoServiceMock() }] - }) - .compileComponents(); + providers: [{ provide: VideoService, useValue: new VideoServiceMock() }], + }).compileComponents(); fixture = TestBed.createComponent(VideoComponent); component = fixture.componentInstance; videoService = TestBed.inject(VideoService); + router = TestBed.inject(Router); }); it('should create', () => { expect(component).toBeTruthy(); }); - it('shoud call findAll', () => { - spyOn(component, 'findAll'); + it('should call getVideos ', () => { + spyOn(component, 'getVideos'); fixture.detectChanges(); - expect(component.findAll).toHaveBeenCalled(); - }) + expect(component.getVideos).toHaveBeenCalled(); + }); - it('should call findAll and return a list of videos', () => { - const mySpy = spyOn(videoService, 'findAll').and.callThrough(); - component.findAll(); + it('should call getVideos and return a list of videos', () => { + const mySpy = spyOn(component, 'getVideos').and.callThrough(); + component.getVideos(); expect(mySpy).toHaveBeenCalled(); + expect(component.unbTvVideos).toEqual(mockData); }); - it('should call findAll and return an error', () => { - const mySpy = spyOn(videoService, 'findAll').and.returnValue(throwError(() => new Error('Erro'))); - component.findAll(); + it('should call getVideos and return an error', () => { + const mySpy = spyOn(videoService, 'getVideosCatalog').and.returnValue( + throwError(() => new Error('Erro')) + ); + component.getVideos(); + expect(component.unbTvVideos).toEqual([]); expect(mySpy).toHaveBeenCalled(); }); + + it('should call returnToCatalog when the button is clicked', () => { + const navigateSpy = spyOn(router, 'navigate'); + component.returnToCatalog(); + expect(navigateSpy).toHaveBeenCalledWith(['/catalog']); + }); }); diff --git a/src/app/pages/video/video.component.ts b/src/app/pages/video/video.component.ts index dcff846d..d7ba5837 100644 --- a/src/app/pages/video/video.component.ts +++ b/src/app/pages/video/video.component.ts @@ -1,33 +1,34 @@ import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; import { VideoService } from '../../services/video.service'; import { IVideo } from 'src/shared/model/video.model'; -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { IEduplayVideosByInstitution } from 'src/shared/model/eduplay-by-institution.model'; @Component({ selector: 'app-video', templateUrl: './video.component.html', styleUrls: ['./video.component.css'], }) - export class VideoComponent implements OnInit { - videosEduplay: IVideo[] = []; + unbTvVideos: IVideo[] = []; - constructor(private videoService: VideoService) { } + constructor(private videoService: VideoService, private router: Router) {} ngOnInit(): void { - this.findAll(); + this.getVideos(); } - findAll(): void { - this.videoService.findAll().subscribe({ - next: (data) => { - this.videosEduplay = data.body?.videoList || []; + getVideos(): void { + this.videoService.getVideosCatalog().subscribe({ + next: (videos) => { + this.unbTvVideos = videos; }, error: (error) => { - console.log(error); + console.error(error); }, - } - ); + }); + } + + returnToCatalog(): void { + this.router.navigate(['/catalog']); } } diff --git a/src/app/services/auth.service.spec.ts b/src/app/services/auth.service.spec.ts index f5736b2e..83b00de8 100644 --- a/src/app/services/auth.service.spec.ts +++ b/src/app/services/auth.service.spec.ts @@ -118,10 +118,24 @@ describe('AuthService', () => { req.flush(userResponse); }); + it('should refresh token', () => { + const dummyResponse = { access_token: 'dummyToken' }; + + service.refreshToken().subscribe(res => { + expect(res).toEqual(dummyResponse); + }); + + const req = httpMock.expectOne(`${service.usersAPIURL}/auth/refresh`); + expect(req.request.method).toBe('POST'); + req.flush(dummyResponse); + }); + it('should logout', () => { localStorage.setItem('token', 'testtoken'); + const navigateSpy = spyOn(service['router'], 'navigate'); service.logout(); expect(localStorage.getItem('token')).toBeNull(); + expect(navigateSpy).toHaveBeenCalledWith(['/loginsocial']); }); - + }); diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index c91004b4..6a2bd524 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -21,6 +21,10 @@ export class AuthService { return this.http.post(`${this.usersAPIURL}/auth/login`, user); } + loginSocialUser(userSocialData: any): Observable { + return this.http.post(`${this.usersAPIURL}/auth/login/social`, userSocialData); + } + activeAccount(emailCode: any): Observable { return this.http.patch(`${this.usersAPIURL}/auth/activate-account`, emailCode); } @@ -45,9 +49,13 @@ export class AuthService { const token = localStorage.getItem('token'); return !!token; } - + + refreshToken(): Observable { + return this.http.post(`${this.usersAPIURL}/auth/refresh`, null); + } + logout(): void { localStorage.removeItem('token'); - this.router.navigate(['/login']); + this.router.navigate(['/loginsocial']); } } diff --git a/src/app/services/video.service.spec.ts b/src/app/services/video.service.spec.ts index accb7896..465edd9d 100644 --- a/src/app/services/video.service.spec.ts +++ b/src/app/services/video.service.spec.ts @@ -3,9 +3,153 @@ import { HttpClientTestingModule, HttpTestingController, } from '@angular/common/http/testing'; -import { EDUPLAY_API_URL, UNB_ID } from 'src/app/app.constant'; +import { + EDUPLAY_API_URL, + UNB_ID, + VIDEOS_LIMIT, + VIDEOS_ORDER, +} from 'src/app/app.constant'; import { EDUPLAY_CLIENT_KEY } from '../environment/environment'; import { VideoService } from './video.service'; +import { IVideo } from 'src/shared/model/video.model'; + +const mockData: IVideo[] = [ + { + id: 190985, + title: 'Esboços: Luiz Gallina', + description: + '


A curiosidade de Luiz Gallina em descobrir e entender como mecanismos e processos funcionam dá origem a uma obra composta por gravuras, pinturas e esculturas - abordando temas tão diversos quanto as árvores da paisagem de Brasília, a alquimia e os sonhos colecionados de forma escrita pelo artista durante anos.



', + keywords: 'Esboços; Luiz Gallina; UnBTV;', + visibility: 'PUBLIC', + duration: 1527827, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 20, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1699445118833.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190984, + title: 'Esboços: Ricardo Caldeira', + description: + '


Quando Ricardo Caldeira desenha parece dançar imprimindo na tela um traço marcante que fala sobre afeto, negritude, ancestralidade. Aqui o artista relembra sua infância e adolescência, os primeiros trabalhos e a relação com a região de São Sebastião, local que o influenciou e que hoje ele transforma por meio da participação em coletivos culturais.



', + keywords: 'Esboços; Ricardo Caldeira; UnBTV;', + visibility: 'PUBLIC', + duration: 1525621, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 14, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1699445004278.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190334, + title: 'Esboços | Fernanda Pacca', + description: + '


Confetes, braçadeiras de náilon, botões, linhas de costuras compõe a palheta de cores que constroem surpreendentes figuras humanas na obra de Fernanda Pacca. Trabalhos que impressionam pela forma e abordam diversos temas como o fascínio pelo anatomia, a repressão - tanto estética quanto social - e a violência contra a mulher. 





', + keywords: 'Fernanda Pacca; Esboços; unbtv;', + visibility: 'PUBLIC', + duration: 1550174, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 15, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1698243181940.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190333, + title: 'Esboços | Valéria Pena-Costa', + description: + '


Poeira, insetos, objetos em deterioração são, ao mesmo tempo, tema e matéria-prima para a obra de Valéria Pena-Costa em um trabalho que reflete a inevitável passagem do tempo, as memórias de infância, os medos passados e presentes em diferentes formas de expressão.


', + keywords: 'Valéria Pena-Costa; Esboços; unbtv;', + visibility: 'PUBLIC', + duration: 1547193, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 23, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1698243362095.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, + { + id: 190324, + title: 'Esboços: TNHA', + description: + '


A busca por uma identidade que integre raízes negras, indígenas e latino-americanas cria uma obra que resgata ancestralidade, de olho sempre nas injustiças históricas. Seja espalhando rostos em grafites pelo Distrito Federal para suavizar o caminho de todo dia; seja tatuando ou fazendo ilustrações, TNHA tem um traço singular que carrega todo um aprendizado de andanças por diferentes comunidades e vivências familiares. 


', + keywords: 'esboços; Tnha; unbtv;', + visibility: 'PUBLIC', + duration: 1550383, + embed: + '', + generateLibras: true, + generateSubtitle: true, + qtAccess: 14, + qtLikes: 0, + images: [ + { + type: 'DEFAULT', + href: 'https://eduplay.rnp.br/portal/assets/videos/images/1698146507222.jpg', + }, + ], + channels: [ + { + id: 190265, + name: 'UnBTV', + }, + ], + }, +]; describe('VideoService', () => { let service: VideoService; @@ -269,7 +413,7 @@ describe('VideoService', () => { }); const req = httpMock.expectOne( - `${EDUPLAY_API_URL}video?institution=${UNB_ID}` + `${EDUPLAY_API_URL}video?institution=${UNB_ID}&limit=${VIDEOS_LIMIT}&order=${VIDEOS_ORDER}` ); expect(req.request.method).toBe('GET'); expect(req.request.headers.get('clientkey')).toBe(EDUPLAY_CLIENT_KEY); @@ -331,4 +475,27 @@ describe('VideoService', () => { req.flush(mockData); }); }); + + describe('setVideosCatalog', () => { + it('should set videos catalog', () => { + const mockVideos = mockData; + + service.setVideosCatalog(mockVideos); + + service.getVideosCatalog().subscribe((videos) => { + expect(videos).toEqual(mockVideos); + }); + }); + }); + + describe('getVideosCatalog', () => { + it('should get videos catalog', () => { + const mockVideos = mockData; + service.setVideosCatalog(mockVideos); + + service.getVideosCatalog().subscribe((videos) => { + expect(videos).toEqual(mockVideos); + }); + }); + }); }); diff --git a/src/app/services/video.service.ts b/src/app/services/video.service.ts index 896b483b..95b7964c 100644 --- a/src/app/services/video.service.ts +++ b/src/app/services/video.service.ts @@ -1,17 +1,19 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { EDUPLAY_API_URL, UNB_ID } from 'src/app/app.constant'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { + EDUPLAY_API_URL, + UNB_ID, + VIDEOS_LIMIT, + VIDEOS_ORDER, +} from 'src/app/app.constant'; import { IVideo } from 'src/shared/model/video.model'; import { IEduplayVideosByInstitution } from 'src/shared/model/eduplay-by-institution.model'; -import { IVideoVersion } from 'src/shared/model/video-version.model'; import { EDUPLAY_CLIENT_KEY } from '../environment/environment'; type VideoResponseType = HttpResponse; -type VideoArrayResponseType = HttpResponse; type EduplayByInstitutionResponseType = HttpResponse; -type VideoVersionResponseType = HttpResponse; @Injectable({ providedIn: 'root', @@ -20,13 +22,16 @@ export class VideoService { public resourceUrl = EDUPLAY_API_URL + 'video'; public eduplayClientKey = EDUPLAY_CLIENT_KEY; public unbId = UNB_ID; + public limit = VIDEOS_LIMIT; + public order = VIDEOS_ORDER; + private selectedCatalogProgram = new BehaviorSubject([]); constructor(private http: HttpClient) {} findAll(): Observable { let headers = new HttpHeaders({ clientkey: this.eduplayClientKey }); return this.http.get( - `${this.resourceUrl}?institution=${this.unbId}`, + `${this.resourceUrl}?institution=${this.unbId}&limit=${this.limit}&order=${this.order}`, { headers: headers, observe: 'response' } ); } @@ -39,4 +44,12 @@ export class VideoService { observe: 'response', }); } + + setVideosCatalog(videos: IVideo[]) { + this.selectedCatalogProgram.next(videos); + } + + getVideosCatalog(): Observable { + return this.selectedCatalogProgram.asObservable(); + } } diff --git a/src/assets/email-svgrepo-com.svg b/src/assets/email-svgrepo-com.svg new file mode 100644 index 00000000..5fa1e9e3 --- /dev/null +++ b/src/assets/email-svgrepo-com.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/facebook-icon-white-png.png b/src/assets/facebook-icon-white-png.png new file mode 100644 index 00000000..bade5046 Binary files /dev/null and b/src/assets/facebook-icon-white-png.png differ diff --git a/src/assets/fonts/Helvetica-Bold.ttf b/src/assets/fonts/Helvetica-Bold.ttf new file mode 100644 index 00000000..332b66ca Binary files /dev/null and b/src/assets/fonts/Helvetica-Bold.ttf differ diff --git a/src/assets/fonts/Helvetica.ttf b/src/assets/fonts/Helvetica.ttf new file mode 100644 index 00000000..718f22d4 Binary files /dev/null and b/src/assets/fonts/Helvetica.ttf differ diff --git a/src/assets/imgs/catalog-thumbs/1.Fala-Jovem.jpg b/src/assets/imgs/catalog-thumbs/1.Fala-Jovem.jpg new file mode 100644 index 00000000..bb8f872a Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/1.Fala-Jovem.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/10.Explique-sua-Tese.jpg b/src/assets/imgs/catalog-thumbs/10.Explique-sua-Tese.jpg new file mode 100644 index 00000000..4a67fb2f Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/10.Explique-sua-Tese.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/11.Fazendo-Ciencia-Formando-Cientistas.jpg b/src/assets/imgs/catalog-thumbs/11.Fazendo-Ciencia-Formando-Cientistas.jpg new file mode 100644 index 00000000..b18c6e0c Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/11.Fazendo-Ciencia-Formando-Cientistas.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/12.Radar-da-Extensao.jpg b/src/assets/imgs/catalog-thumbs/12.Radar-da-Extensao.jpg new file mode 100644 index 00000000..67d62f8e Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/12.Radar-da-Extensao.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/13.Se-Liga-no-PAS.jpg b/src/assets/imgs/catalog-thumbs/13.Se-Liga-no-PAS.jpg new file mode 100644 index 00000000..27857f03 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/13.Se-Liga-no-PAS.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/14.UnBTV-Ciencia.png b/src/assets/imgs/catalog-thumbs/14.UnBTV-Ciencia.png new file mode 100644 index 00000000..21f0bb75 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/14.UnBTV-Ciencia.png differ diff --git a/src/assets/imgs/catalog-thumbs/15.Universidade-Para-Que.jpg b/src/assets/imgs/catalog-thumbs/15.Universidade-Para-Que.jpg new file mode 100644 index 00000000..865b8467 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/15.Universidade-Para-Que.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/16.Emcantos.jpg b/src/assets/imgs/catalog-thumbs/16.Emcantos.jpg new file mode 100644 index 00000000..4c05b48a Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/16.Emcantos.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/17.Casa-do-Som.jpg b/src/assets/imgs/catalog-thumbs/17.Casa-do-Som.jpg new file mode 100644 index 00000000..c7a89779 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/17.Casa-do-Som.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/18.Esbocos.jpg b/src/assets/imgs/catalog-thumbs/18.Esbocos.jpg new file mode 100644 index 00000000..1d24ab45 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/18.Esbocos.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/19.Exclusiva.jpg b/src/assets/imgs/catalog-thumbs/19.Exclusiva.jpg new file mode 100644 index 00000000..41831288 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/19.Exclusiva.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/2.Informe-UnB.jpg b/src/assets/imgs/catalog-thumbs/2.Informe-UnB.jpg new file mode 100644 index 00000000..41404142 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/2.Informe-UnB.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/20.Floresta-de-Gente.jpg b/src/assets/imgs/catalog-thumbs/20.Floresta-de-Gente.jpg new file mode 100644 index 00000000..81a59152 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/20.Floresta-de-Gente.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/21.Guia-do-Calouro.jpg b/src/assets/imgs/catalog-thumbs/21.Guia-do-Calouro.jpg new file mode 100644 index 00000000..9712a530 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/21.Guia-do-Calouro.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/22.Memorias-de-Paulo-Freire.jpg b/src/assets/imgs/catalog-thumbs/22.Memorias-de-Paulo-Freire.jpg new file mode 100644 index 00000000..0e79e803 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/22.Memorias-de-Paulo-Freire.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/23.Os-desafios-das-eleicoes2022.jpg b/src/assets/imgs/catalog-thumbs/23.Os-desafios-das-eleicoes2022.jpg new file mode 100644 index 00000000..dabdaf0a Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/23.Os-desafios-das-eleicoes2022.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/24.Podcast-Vida-de-Estudante.jpg b/src/assets/imgs/catalog-thumbs/24.Podcast-Vida-de-Estudante.jpg new file mode 100644 index 00000000..afd9e8ec Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/24.Podcast-Vida-de-Estudante.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/25.Serie-Arquitetura.jpg b/src/assets/imgs/catalog-thumbs/25.Serie-Arquitetura.jpg new file mode 100644 index 00000000..c28459fb Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/25.Serie-Arquitetura.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/26.Mini-Doc.jpg b/src/assets/imgs/catalog-thumbs/26.Mini-Doc.jpg new file mode 100644 index 00000000..b16679ca Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/26.Mini-Doc.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/27.Documentarios.jpg b/src/assets/imgs/catalog-thumbs/27.Documentarios.jpg new file mode 100644 index 00000000..80537600 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/27.Documentarios.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/28.Pitadas-do-Cerrado.jpg b/src/assets/imgs/catalog-thumbs/28.Pitadas-do-Cerrado.jpg new file mode 100644 index 00000000..a2c5cc36 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/28.Pitadas-do-Cerrado.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/3.Zapping.jpg b/src/assets/imgs/catalog-thumbs/3.Zapping.jpg new file mode 100644 index 00000000..45d61426 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/3.Zapping.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/4.Brasil-em-Questao.jpg b/src/assets/imgs/catalog-thumbs/4.Brasil-em-Questao.jpg new file mode 100644 index 00000000..2053e3f5 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/4.Brasil-em-Questao.jpg differ diff --git "a/src/assets/imgs/catalog-thumbs/5.Dia\314\201logos.jpg" "b/src/assets/imgs/catalog-thumbs/5.Dia\314\201logos.jpg" new file mode 100644 index 00000000..df2be674 Binary files /dev/null and "b/src/assets/imgs/catalog-thumbs/5.Dia\314\201logos.jpg" differ diff --git a/src/assets/imgs/catalog-thumbs/6.Tirando-de-Letra.jpg b/src/assets/imgs/catalog-thumbs/6.Tirando-de-Letra.jpg new file mode 100644 index 00000000..eda8be79 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/6.Tirando-de-Letra.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/7.UnBTV-Entrevista.jpg b/src/assets/imgs/catalog-thumbs/7.UnBTV-Entrevista.jpg new file mode 100644 index 00000000..65c387b0 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/7.UnBTV-Entrevista.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/8.Vasto-Mundo.jpg b/src/assets/imgs/catalog-thumbs/8.Vasto-Mundo.jpg new file mode 100644 index 00000000..607d3bc8 Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/8.Vasto-Mundo.jpg differ diff --git a/src/assets/imgs/catalog-thumbs/9.Vozes-Diplomaticas.jpg b/src/assets/imgs/catalog-thumbs/9.Vozes-Diplomaticas.jpg new file mode 100644 index 00000000..c7e212bf Binary files /dev/null and b/src/assets/imgs/catalog-thumbs/9.Vozes-Diplomaticas.jpg differ diff --git a/src/assets/share.png b/src/assets/share.png new file mode 100644 index 00000000..65caca70 Binary files /dev/null and b/src/assets/share.png differ diff --git a/src/shared/model/catalog.model.ts b/src/shared/model/catalog.model.ts new file mode 100644 index 00000000..d532d81f --- /dev/null +++ b/src/shared/model/catalog.model.ts @@ -0,0 +1,61 @@ +import { IVideo } from './video.model'; + +export class Catalog { + journalism: Journalism = new Journalism(); + interviews: Interviews = new Interviews(); + researchAndScience: ResearchAndScience = new ResearchAndScience(); + artAndCulture: ArtAndCulture = new ArtAndCulture(); + specialSeries: SpecialSeries = new SpecialSeries(); + documentaries: Documentaries = new Documentaries(); + varieties: Varieties = new Varieties(); + unbtv: IVideo[] = []; +} + +class Journalism { + falaJovem: IVideo[] = []; + informeUnB: IVideo[] = []; + zapping: IVideo[] = []; +} + +class Interviews { + brasilEmQuestao: IVideo[] = []; + dialogos: IVideo[] = []; + tirandoDeLetra: IVideo[] = []; + entrevistas: IVideo[] = []; + vastoMundo: IVideo[] = []; + vozesDiplomaticas: IVideo[] = []; +} + +class ResearchAndScience { + expliqueSuaTese: IVideo[] = []; + fazendoCiencia: IVideo[] = []; + radarDaExtencao: IVideo[] = []; + seLigaNoPAS: IVideo[] = []; + unbTvCiencia: IVideo[] = []; + universidadeParaQue: IVideo[] = []; +} + +class ArtAndCulture { + emCantos: IVideo[] = []; + casaDoSom: IVideo[] = []; + esbocos: IVideo[] = []; + exclusiva: IVideo[] = []; +} + +class SpecialSeries { + florestaDeGente: IVideo[] = []; + guiaDoCalouro: IVideo[] = []; + memoriasPauloFreire: IVideo[] = []; + desafiosDasEleicoes: IVideo[] = []; + vidaDeEstudante: IVideo[] = []; + arquiteturaICC: IVideo[] = []; +} + +class Documentaries { + miniDoc: IVideo[] = []; + documentaries: IVideo[] = []; +} + +class Varieties { + pitadasDoCerrado: IVideo[] = []; +} diff --git a/src/shared/model/channel.model.ts b/src/shared/model/channel.model.ts new file mode 100644 index 00000000..382f0623 --- /dev/null +++ b/src/shared/model/channel.model.ts @@ -0,0 +1,8 @@ +export interface IChannel { + id: number; + name: string; +} + +export class Channel implements IChannel { + constructor(public id: number, public name: string) {} +} diff --git a/src/shared/model/video.model.ts b/src/shared/model/video.model.ts index de3178a0..d35d3a2d 100644 --- a/src/shared/model/video.model.ts +++ b/src/shared/model/video.model.ts @@ -1,33 +1,36 @@ -import { IImage } from "./image.model"; +import { IChannel } from './channel.model'; +import { IImage } from './image.model'; export interface IVideo { - id?: number; - title?: string; - description?: string; - keywords?: string; - visibility?: string; - duration?: number; - generateLibras?: boolean; - generateSubtitle?: boolean; - qtAccess?: number; - qtLikes?: number; - images?: IImage[]; - embed?: string; + id?: number; + title?: string; + description?: string; + keywords?: string; + visibility?: string; + duration?: number; + generateLibras?: boolean; + generateSubtitle?: boolean; + qtAccess?: number; + qtLikes?: number; + images?: IImage[]; + embed?: string; + channels?: IChannel[]; } export class Video implements IVideo { - constructor( - public id?: number, - public title?: string, - public description?: string, - public keywords?: string, - public visibility?: string, - public duration?: number, - public generateLibras?: boolean, - public generateSubtitle?: boolean, - public qtAccess?: number, - public qtLikes?: number, - public images?: IImage[], - public embed?: string, - ) { } -} \ No newline at end of file + constructor( + public id?: number, + public title?: string, + public description?: string, + public keywords?: string, + public visibility?: string, + public duration?: number, + public generateLibras?: boolean, + public generateSubtitle?: boolean, + public qtAccess?: number, + public qtLikes?: number, + public images?: IImage[], + public embed?: string, + public channels?: IChannel[] + ) {} +} diff --git a/tailwind.config.js b/tailwind.config.js index 599586eb..bdad8f63 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -7,12 +7,13 @@ module.exports = { custom: ["unb-pro", "sans-serif"], }, backgroundColor: { - 'blue-brand': '#0087C8', + "blue-brand": "#0087C8", }, textColor: { - 'blue-brand': '#0087C8', + "grey-brand": "#3c3c3b", + "blue-brand": "#0087C8", }, }, plugins: [], - } -} + }, +}; diff --git a/tsconfig.app.json b/tsconfig.app.json index 374cc9d2..f54aee6e 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -3,7 +3,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", - "types": [] + "types": ["gapi", "gapi.auth2"] }, "files": [ "src/main.ts"