| <div class="site"> | <div class="site"> | ||||
| <header> | |||||
| <app-header></app-header> | |||||
| </header> | |||||
| <main> | <main> | ||||
| <app-news-dashboard></app-news-dashboard> | <app-news-dashboard></app-news-dashboard> | ||||
| </main> | </main> |
| @import "../variables.scss"; | @import "../variables.scss"; | ||||
| main { | main { | ||||
| padding-left: $footer-height; | |||||
| padding-top: $footer-height; | |||||
| padding: 0px; | |||||
| margin: 0px; | |||||
| padding-bottom: $footer-height; | padding-bottom: $footer-height; | ||||
| } | } | ||||
| .site { | .site { | ||||
| height: auto; | height: auto; | ||||
| min-height: 100%; | |||||
| } | } | ||||
| footer { | footer { |
| import { BrowserModule } from '@angular/platform-browser'; | import { BrowserModule } from '@angular/platform-browser'; | ||||
| import { NgModule } from '@angular/core'; | import { NgModule } from '@angular/core'; | ||||
| import { FormsModule } from '@angular/forms'; | |||||
| import { AppRoutingModule } from './app-routing.module'; | import { AppRoutingModule } from './app-routing.module'; | ||||
| import { AppComponent } from './app.component'; | import { AppComponent } from './app.component'; | ||||
| import { TabsComponent } from './misc/tab/tabs/tabs.component'; | import { TabsComponent } from './misc/tab/tabs/tabs.component'; | ||||
| import { TabComponent } from './misc/tab/tab/tab.component'; | import { TabComponent } from './misc/tab/tab/tab.component'; | ||||
| import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; | import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; | ||||
| import { NewsTeaserComponent } from './news/news-teaser/news-teaser.component'; | |||||
| @NgModule({ | @NgModule({ | ||||
| AllNewsDashboardComponent, | AllNewsDashboardComponent, | ||||
| TabsComponent, | TabsComponent, | ||||
| TabComponent, | TabComponent, | ||||
| NewsTeaserComponent, | |||||
| ], | ], | ||||
| imports: [ | imports: [ | ||||
| FormsModule, | |||||
| BrowserModule.withServerTransition({ appId: 'serverApp' }), | BrowserModule.withServerTransition({ appId: 'serverApp' }), | ||||
| AppRoutingModule, | AppRoutingModule, | ||||
| HttpClientModule, | HttpClientModule, |
| padding-left: 0.5%; | padding-left: 0.5%; | ||||
| padding-top: 0.5%; | padding-top: 0.5%; | ||||
| padding-bottom: 0.5%; | padding-bottom: 0.5%; | ||||
| background-color: $primary-color; | |||||
| background: transparent; | |||||
| margin: 0px; | margin: 0px; | ||||
| font-size: 20px; | font-size: 20px; | ||||
| text-align: left; | text-align: left; |
| <div class="header"> | |||||
| <fa-icon [icon]="faNewspaper"></fa-icon> | |||||
| News Page | |||||
| </div> |
| .header { | .header { | ||||
| color: white; | color: white; | ||||
| background: linear-gradient(48deg, $detail-color, $secondary-color, $primary-color); | |||||
| background: transparent; | |||||
| margin: 0px; | margin: 0px; | ||||
| font-size: 30px; | font-size: 30px; | ||||
| padding: 1em 1em; | padding: 1em 1em; |
| .inactive { | .inactive { | ||||
| display: none; | display: none; | ||||
| } | } | ||||
| .pane { | |||||
| width: 100%; | |||||
| } |
| import { Component, OnInit, ContentChildren, Input } from '@angular/core'; | |||||
| import { Component, OnInit, ContentChildren, Input, ViewChildren, TemplateRef } from '@angular/core'; | |||||
| import { TabsComponent } from '../tabs/tabs.component'; | import { TabsComponent } from '../tabs/tabs.component'; | ||||
| import { Template } from '@angular/compiler/src/render3/r3_ast'; | |||||
| @Component({ | @Component({ | ||||
| templateUrl: 'tab.component.html', | templateUrl: 'tab.component.html', |
| <div class="tab-container"> | <div class="tab-container"> | ||||
| <div class="flex row"> | |||||
| <div | |||||
| <div class="flex-container header"> | |||||
| <p class="header-icon"> | |||||
| <fa-icon [icon]="faNewspaper"></fa-icon> | |||||
| </p> | |||||
| <p class="title">News Page</p> | |||||
| <p class="spacer"></p> | |||||
| <p class="spacer"></p> | |||||
| <p | |||||
| *ngFor="let tab of tabs" | *ngFor="let tab of tabs" | ||||
| (click)="selectTab(tab)" | (click)="selectTab(tab)" | ||||
| [class.active]="tab?.active" | [class.active]="tab?.active" | ||||
| class="flex col tab-title" | |||||
| class="tab-title" | |||||
| > | > | ||||
| <p>{{ tab?.title }}</p> | |||||
| </div> | |||||
| {{ tab?.title }} | |||||
| </p> | |||||
| </div> | </div> | ||||
| <div class="tab-content flex row" > | |||||
| <ng-content></ng-content> | |||||
| <div class="tab-content flex flex-row"> | |||||
| <ng-content></ng-content> | |||||
| </div> | </div> | ||||
| </div> | </div> |
| $border-radius: 12.5px; | |||||
| @import "../../../../variables.scss"; | @import "../../../../variables.scss"; | ||||
| .tab-title { | .tab-title { | ||||
| min-width: 6em; | |||||
| cursor: pointer; | cursor: pointer; | ||||
| padding-left: 5px; | |||||
| padding-right: 5px; | |||||
| border-top: solid $border-color 1px; | |||||
| margin-bottom: 0px; | |||||
| border-top-left-radius: 12.5px; | |||||
| border-top-right-radius: 12.5px; | |||||
| background-color: $primary-color; | |||||
| opacity: 0.4; | opacity: 0.4; | ||||
| transition: 0.3s; | transition: 0.3s; | ||||
| padding: 1em; | |||||
| } | } | ||||
| .title { | |||||
| justify-self: flex-start; | |||||
| align-items: self-start; | |||||
| } | |||||
| .tab-title:active, .active { | |||||
| .tab-title:active, | |||||
| .active { | |||||
| opacity: 1; | opacity: 1; | ||||
| } | } | ||||
| .tab-title:hover { | .tab-title:hover { | ||||
| opacity: 0.8 | |||||
| opacity: 0.8; | |||||
| } | } | ||||
| .tab-content { | .tab-content { | ||||
| border: 5px solid $primary-color; | |||||
| border-top-right-radius: 12.5px; | |||||
| border-top-right-radius: 12.5px; | border-top-right-radius: 12.5px; | ||||
| border-bottom-left-radius: 12.5px; | border-bottom-left-radius: 12.5px; | ||||
| border-bottom-right-radius: 12.5px; | border-bottom-right-radius: 12.5px; | ||||
| padding-left: 1em; | |||||
| height: 100%; | |||||
| } | |||||
| .tab-container { | |||||
| border-radius: $border-radius; | |||||
| background: white; | |||||
| } | |||||
| .header-icon { | |||||
| padding: 1em; | |||||
| } | |||||
| .header { | |||||
| justify-content: space-between; | |||||
| align-items: spac; | |||||
| height: 3em; | |||||
| flex-direction: row nowrap; | |||||
| font-size: $header-font-size; | |||||
| color: white; | |||||
| background: linear-gradient(48deg, $detail-color, $secondary-color, $primary-color); | |||||
| } | } |
| import { Component, OnInit, ContentChildren, QueryList, AfterContentInit } from '@angular/core'; | import { Component, OnInit, ContentChildren, QueryList, AfterContentInit } from '@angular/core'; | ||||
| import { TabComponent } from '../tab/tab.component'; | import { TabComponent } from '../tab/tab.component'; | ||||
| import { faNewspaper, faFilter } from '@fortawesome/free-solid-svg-icons'; | |||||
| import { from } from 'rxjs'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-tabs', | selector: 'app-tabs', | ||||
| styleUrls: ['./tabs.component.scss'] | styleUrls: ['./tabs.component.scss'] | ||||
| }) | }) | ||||
| export class TabsComponent implements OnInit, AfterContentInit { | export class TabsComponent implements OnInit, AfterContentInit { | ||||
| faNewspaper = faNewspaper; | |||||
| @ContentChildren(TabComponent) tabs: QueryList<TabComponent>; | @ContentChildren(TabComponent) tabs: QueryList<TabComponent>; | ||||
| @ContentChildren(TabComponent) activeTab: TabComponent; | @ContentChildren(TabComponent) activeTab: TabComponent; | ||||
| public selectTab(tab: TabComponent): void { | public selectTab(tab: TabComponent): void { | ||||
| } | } | ||||
| protected getTabAnimationDirection(tab: TabComponent): 'right' | 'left' | 'none' { | protected getTabAnimationDirection(tab: TabComponent): 'right' | 'left' | 'none' { | ||||
| debugger; | |||||
| const activeTab = this.tabs.toArray().filter(tab => tab.active)[0]; | const activeTab = this.tabs.toArray().filter(tab => tab.active)[0]; | ||||
| if (activeTab === undefined) { | if (activeTab === undefined) { | ||||
| return 'none'; | return 'none'; | ||||
| return 'none'; | return 'none'; | ||||
| } | } | ||||
| getActiveTab(): TabComponent { | |||||
| return this.tabs.toArray().filter(tab => tab.active)[0]; | |||||
| } | |||||
| ngAfterContentInit(): void { | ngAfterContentInit(): void { | ||||
| // get all active tabs | // get all active tabs | ||||
| this.tabs.toArray().forEach((tab: TabComponent, index) => tab.tabIndex = index); | this.tabs.toArray().forEach((tab: TabComponent, index) => tab.tabIndex = index); |
| export class TopHeadlineSearchDto { | |||||
| sources: string; | |||||
| q: string; | |||||
| qInTitle: string; | |||||
| domains: string; | |||||
| excludeDomains: string; | |||||
| from: Date; | |||||
| to: Date; | |||||
| } |
| import { SortBy } from '../enum/sort-by'; | |||||
| export class NewsSearchDto { | |||||
| sources?: string; | |||||
| q?: string; | |||||
| qInTitle?: string; | |||||
| domains?: string; | |||||
| excludeDomains?: string; | |||||
| from?: Date; | |||||
| to?: Date; | |||||
| sortBy?: SortBy; | |||||
| pageSize?: number; | |||||
| page?: number; | |||||
| apiKey: string; | |||||
| } |
| export enum SortBy { | |||||
| 'Relevancy' = 'relevancy', | |||||
| 'Popularity' = 'popularity', | |||||
| 'PublishedAt' = 'publishedAt', | |||||
| } |
| <p>all-news-dashboard works!</p> | |||||
| <div class="flex-container wrap"> | |||||
| <div class="flex-item" *ngFor="let article of news"> | |||||
| <app-news-teaser [article]="article"></app-news-teaser> | |||||
| </div> | |||||
| </div> |
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| import { NewsService } from '../../service/news.service'; | |||||
| import { Article } from '../../models/model/article'; | |||||
| import { apiKey } from '../../../environments/.api-key'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-all-news-dashboard', | selector: 'app-all-news-dashboard', | ||||
| }) | }) | ||||
| export class AllNewsDashboardComponent implements OnInit { | export class AllNewsDashboardComponent implements OnInit { | ||||
| constructor() { } | |||||
| news: Article[]; | |||||
| constructor(protected newsService: NewsService) { } | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| // this.newsService.searchAllNews({ | |||||
| // apiKey | |||||
| // }).subscribe(response => { | |||||
| // this.news = response.articles; | |||||
| // }); | |||||
| } | } | ||||
| } | } |
| <app-tabs> | <app-tabs> | ||||
| <app-tab title="Top News"> | |||||
| <app-tab style="width: auto" title="Top News"> | |||||
| <app-top-news-dashboard></app-top-news-dashboard> | <app-top-news-dashboard></app-top-news-dashboard> | ||||
| </app-tab> | </app-tab> | ||||
| <app-tab title="All News"> | |||||
| <app-tab style="width: auto" title="All News"> | |||||
| <app-all-news-dashboard></app-all-news-dashboard> | <app-all-news-dashboard></app-all-news-dashboard> | ||||
| </app-tab> | </app-tab> | ||||
| </app-tabs> | </app-tabs> |
| @import "../../../variables.scss"; | |||||
| $border-radius: 12.5px; | |||||
| .dashboard { | |||||
| padding: 0px; | |||||
| background: white; | |||||
| } | |||||
| <div | |||||
| (click)="open()" | |||||
| class="teaser" | |||||
| *ngIf="article.urlToImage && article.content" | |||||
| > | |||||
| <div class="teaser-header"> | |||||
| <div class="teaser-title">{{ article.title }}</div> | |||||
| <div class="teaser-img"> | |||||
| <img | |||||
| [src]="article.urlToImage" | |||||
| *ngIf="article.urlToImage" | |||||
| alt="image of article" | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <div class="teaser-content" *ngIf="article.description"> | |||||
| {{ article.description }} ... <a [href]="article.url"> Read</a> | |||||
| </div> | |||||
| </div> |
| @import "../../../variables.scss"; | |||||
| $teaser-border-radius: 12.5px; | |||||
| $teaser-background: linear-gradient(48deg, rgb(223, 217, 217), white); | |||||
| .teaser-header { | |||||
| min-height: 10em; | |||||
| flex-direction: column; | |||||
| > div.teaser-img { | |||||
| border-top-left-radius: $teaser-border-radius; | |||||
| border-top-right-radius: $teaser-border-radius; | |||||
| background: $teaser-background; | |||||
| display: flex; | |||||
| justify-content: center; | |||||
| align-items: center; | |||||
| > img { | |||||
| padding: 5px; | |||||
| max-width: 40em; | |||||
| max-height: 20em; | |||||
| } | |||||
| } | |||||
| > div.teaser-title { | |||||
| padding: 2em; | |||||
| background: transparent; | |||||
| max-width: 40em; | |||||
| text-align: center; | |||||
| font-size: $header-font-size; | |||||
| } | |||||
| > div.teaser-title::after { | |||||
| content: ""; | |||||
| background-color: $detail-color; | |||||
| display: block; | |||||
| margin: 2rem 0; | |||||
| height: 2px; | |||||
| width: 3.75rem; | |||||
| } | |||||
| } | |||||
| .teaser { | |||||
| cursor: pointer; | |||||
| border: 1px solid $border-color; | |||||
| width: 45em; | |||||
| min-height: 20em; | |||||
| border-radius: $teaser-border-radius; | |||||
| } | |||||
| @-webkit-keyframes hover { | |||||
| 100% { | |||||
| border: 1px white; | |||||
| } | |||||
| } | |||||
| @keyframes hover { | |||||
| 100% { | |||||
| border: 1px solid white; | |||||
| } | |||||
| } | |||||
| .teaser:hover { | |||||
| -webkit-animation: hover 0.3s cubic-bezier(0.39, 0.575, 0.565, 1) both; | |||||
| animation: hover 0.3s cubic-bezier(0.39, 0.575, 0.565, 1) both; | |||||
| } | |||||
| .teaser-content { | |||||
| background: $teaser-background; | |||||
| border-bottom-left-radius: $teaser-border-radius; | |||||
| border-bottom-right-radius: $teaser-border-radius; | |||||
| min-width: 10em; | |||||
| padding: 5px; | |||||
| } | |||||
| .teaser-footer { | |||||
| height: 2em; | |||||
| padding: 5px; | |||||
| } | |||||
| .btn { | |||||
| border-radius: 2.5px; | |||||
| background-color: $button-color; | |||||
| color: white; | |||||
| padding: 5px; | |||||
| } | |||||
| a { | |||||
| color: $detail-color; | |||||
| text-decoration: none; | |||||
| } |
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { NewsTeaserComponent } from './news-teaser.component'; | |||||
| describe('NewsTeaserComponent', () => { | |||||
| let component: NewsTeaserComponent; | |||||
| let fixture: ComponentFixture<NewsTeaserComponent>; | |||||
| beforeEach(async(() => { | |||||
| TestBed.configureTestingModule({ | |||||
| declarations: [ NewsTeaserComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| })); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(NewsTeaserComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import { Component, OnInit, Input } from '@angular/core'; | |||||
| import { Article } from '../../models/model/article'; | |||||
| @Component({ | |||||
| selector: 'app-news-teaser', | |||||
| templateUrl: './news-teaser.component.html', | |||||
| styleUrls: ['./news-teaser.component.scss'] | |||||
| }) | |||||
| export class NewsTeaserComponent implements OnInit { | |||||
| @Input() article: Article; | |||||
| constructor() { } | |||||
| ngOnInit(): void { | |||||
| } | |||||
| open(): void { | |||||
| window.open(this.article.url); | |||||
| } | |||||
| } |
| <p>top-news-dashboard works!</p> | |||||
| <div class="title flex-container"> | |||||
| Top | |||||
| <ng-container *ngIf="selectedCategory"> | |||||
| {{ selectedCategory.key }} | |||||
| </ng-container> | |||||
| News | |||||
| <ng-container *ngIf="selectedCountry" | |||||
| >in {{ selectedCountry.key }} | |||||
| <img | |||||
| class="flag" | |||||
| src="https://www.countryflags.io/{{ | |||||
| selectedCountry.value | |||||
| }}/shiny/64.png" | |||||
| /></ng-container> | |||||
| </div> | |||||
| <div class="flex-container filter-bar" *ngIf="news"> | |||||
| <div class="filter"> | |||||
| <label for="search">Search: </label> | |||||
| <input | |||||
| (change)="upateDto()" | |||||
| name="search" | |||||
| id="search" | |||||
| [(ngModel)]="searchDto.q" | |||||
| /> | |||||
| </div> | |||||
| <div class="filter"> | |||||
| <label for="country">Country: </label> | |||||
| <select | |||||
| (change)="upateDto()" | |||||
| name="country" | |||||
| id="country" | |||||
| [(ngModel)]="selectedCountry" | |||||
| > | |||||
| <option | |||||
| *ngFor="let country of CountryCodes | keyvalue" | |||||
| [ngValue]="country" | |||||
| > | |||||
| {{ country.key }} | |||||
| </option> | |||||
| </select> | |||||
| </div> | |||||
| <div class="filter"> | |||||
| <label for="category">Category: </label> | |||||
| <select | |||||
| (change)="upateDto()" | |||||
| name="category" | |||||
| id="category" | |||||
| [(ngModel)]="selectedCategory" | |||||
| > | |||||
| <option *ngFor="let category of Category | keyvalue" [ngValue]="category"> | |||||
| {{ category.key }} | |||||
| </option> | |||||
| </select> | |||||
| </div> | |||||
| </div> | |||||
| <div class="flex-container wrap" *ngIf="news?.length; else notingFound"> | |||||
| <div class="flex-item" *ngFor="let article of news"> | |||||
| <app-news-teaser [article]="article"></app-news-teaser> | |||||
| </div> | |||||
| </div> | |||||
| <ng-template #notingFound> | |||||
| <div class="flex-container not-found"> | |||||
| noting found | |||||
| </div> | |||||
| </ng-template> |
| @import "../../../variables.scss"; | |||||
| .filter-bar { | |||||
| height: 5em; | |||||
| margin: 2em; | |||||
| display: flex; | |||||
| flex-flow: row; | |||||
| justify-content: space-evenly; | |||||
| align-items: center; | |||||
| border: 1px solid $border-color; | |||||
| } | |||||
| .filter { | |||||
| height: 2em; | |||||
| } | |||||
| .title { | |||||
| background: transparent; | |||||
| font-size: $header-font-size; | |||||
| } | |||||
| .flag { | |||||
| height: 1em; | |||||
| padding-left: 0.5em; | |||||
| align-self: center; | |||||
| } | |||||
| .not-found { | |||||
| width: 160em; | |||||
| height: 10em; | |||||
| } |
| import { Component, OnInit } from '@angular/core'; | |||||
| import { Component, OnInit, ContentChild } from '@angular/core'; | |||||
| import { Article } from '../../models/model/article'; | import { Article } from '../../models/model/article'; | ||||
| import { NewsService } from '../../service/news.service'; | import { NewsService } from '../../service/news.service'; | ||||
| import { apiKey } from '../../../environments/.api-key'; | import { apiKey } from '../../../environments/.api-key'; | ||||
| import { CountryCode } from '../../models/enum/country-codes'; | import { CountryCode } from '../../models/enum/country-codes'; | ||||
| import { TopHeadlineSearchDto } from '../../models/dto/top-headline-search.dto'; | |||||
| import { Category } from 'src/app/models/enum/category'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-top-news-dashboard', | selector: 'app-top-news-dashboard', | ||||
| styleUrls: ['./top-news-dashboard.component.scss'] | styleUrls: ['./top-news-dashboard.component.scss'] | ||||
| }) | }) | ||||
| export class TopNewsDashboardComponent implements OnInit { | export class TopNewsDashboardComponent implements OnInit { | ||||
| news: Article[]; | news: Article[]; | ||||
| searchDto: TopHeadlineSearchDto = { apiKey }; | |||||
| selectedCountry: { key: string, value: CountryCode }; | |||||
| selectedCategory: { key: string, value: Category }; | |||||
| CountryCodes = CountryCode; | |||||
| Category = Category; | |||||
| constructor(protected newsService: NewsService) { } | constructor(protected newsService: NewsService) { } | ||||
| ngOnInit(): void { | |||||
| this.newsService.getTopNews({ | |||||
| apiKey, | |||||
| country: CountryCode.Germany | |||||
| }).subscribe(response => { | |||||
| upateDto(): void { | |||||
| if (this.selectedCountry?.value) { | |||||
| this.searchDto.country = this.selectedCountry.value; | |||||
| } else { | |||||
| delete this.searchDto.country; | |||||
| } | |||||
| if (this.selectedCategory?.value) { | |||||
| this.searchDto.category = this.selectedCategory.value; | |||||
| } else { | |||||
| delete this.searchDto.category; | |||||
| } | |||||
| this.search(); | |||||
| } | |||||
| search(): void { | |||||
| this.newsService.searchTopNews( | |||||
| this.searchDto | |||||
| ).subscribe(response => { | |||||
| this.news = response.articles; | this.news = response.articles; | ||||
| }); | }); | ||||
| } | } | ||||
| ngOnInit(): void { | |||||
| this.selectedCountry = { key: 'Germany', value: CountryCode.Germany }; | |||||
| this.upateDto(); | |||||
| } | |||||
| } | } |
| import { HttpClient } from '@angular/common/http'; | |||||
| import { Injectable } from '@angular/core'; | import { Injectable } from '@angular/core'; | ||||
| import { HttpClient, HttpParams, HttpResponse, HttpHeaders } from '@angular/common/http'; | |||||
| import { Observable } from 'rxjs'; | |||||
| import { environment } from '../../environments/environment'; | |||||
| import { TopHeadlineSearchDto } from '../models/dto/top-headline-search.dto'; | import { TopHeadlineSearchDto } from '../models/dto/top-headline-search.dto'; | ||||
| import { NewsSearchDto } from '../models/dto/news-search.dto'; | |||||
| import { NewsResponse } from '../models/response/news-response'; | import { NewsResponse } from '../models/response/news-response'; | ||||
| import { environment } from '../../environments/environment'; | |||||
| import { Observable, Subject } from 'rxjs'; | |||||
| import { map, tap } from 'rxjs/operators'; | |||||
| @Injectable({ | @Injectable({ | ||||
| providedIn: 'root' | providedIn: 'root' | ||||
| constructor(protected http: HttpClient) { } | constructor(protected http: HttpClient) { } | ||||
| getTopNews(searchDto: TopHeadlineSearchDto): Observable<NewsResponse> | null { | |||||
| return new Subject<NewsResponse>(); | |||||
| const headers = new HttpHeaders(); | |||||
| return this.http.get<NewsResponse>(`${environment.apiUrl}/top-headlines`, { params: searchDto as any, headers }); | |||||
| searchTopNews(searchDto: TopHeadlineSearchDto): Observable<NewsResponse> | null { | |||||
| return this.http.get<NewsResponse>(`${environment.apiUrl}/top-headlines`, { params: searchDto as any }); | |||||
| } | |||||
| searchAllNews(searchDto: NewsSearchDto): Observable<NewsResponse> | null { | |||||
| return this.http.get<NewsResponse>(`${environment.apiUrl}/everything`, { params: searchDto as any }); | |||||
| } | } | ||||
| } | } |
| @import "./variables.scss"; | @import "./variables.scss"; | ||||
| .flex { | .flex { | ||||
| display: flex; | display: flex; | ||||
| .container { | |||||
| > .container { | |||||
| flex-direction: column; | flex-direction: column; | ||||
| } | } | ||||
| .col { | |||||
| > .col { | |||||
| flex-direction: column; | flex-direction: column; | ||||
| } | } | ||||
| .row { | |||||
| flex-direction: row; | |||||
| } | |||||
| .content { | |||||
| > .content { | |||||
| flex: 1; | flex: 1; | ||||
| } | } | ||||
| } | } | ||||
| .flex-row { | |||||
| flex-direction: row; | |||||
| } | |||||
| .flex-container { | |||||
| padding: 0; | |||||
| margin: 0; | |||||
| list-style: none; | |||||
| display: -webkit-box; | |||||
| display: -moz-box; | |||||
| display: -ms-flexbox; | |||||
| display: -moz-flex; | |||||
| display: -webkit-flex; | |||||
| display: flex; | |||||
| justify-content: center; | |||||
| align-items: center; | |||||
| } | |||||
| .nowrap { | |||||
| -webkit-flex-wrap: nowrap; | |||||
| flex-wrap: nowrap; | |||||
| } | |||||
| .spacer { | |||||
| flex-grow: 1; | |||||
| } | |||||
| .wrap { | |||||
| -webkit-flex-wrap: wrap; | |||||
| flex-wrap: wrap; | |||||
| } | |||||
| .flex-item { | |||||
| padding: 5px; | |||||
| } | |||||
| .col > div { | .col > div { | ||||
| margin: 10px; | margin: 10px; | ||||
| padding: 20px; | padding: 20px; | ||||
| body, | body, | ||||
| html { | html { | ||||
| background-color: $base-color; | |||||
| font-size: $default-font-size; | |||||
| background: linear-gradient(48deg, $detail-color, $secondary-color, $primary-color); | |||||
| height: 100%; | height: 100%; | ||||
| margin: 0px; | margin: 0px; | ||||
| } | } | ||||
| $secondary-color: #8c3cee; | $secondary-color: #8c3cee; | ||||
| $detail-color: #8c3cee; | $detail-color: #8c3cee; | ||||
| $button-color: #8a8095; | |||||
| $text-primary: white; | $text-primary: white; | ||||
| $border-color: white; | |||||
| $border-color: #d8d8d8; | |||||
| $footer-height: 1.2em; | $footer-height: 1.2em; | ||||
| $default-font-size: 15px; | |||||
| $header-font-size: 25px; |