PUNKT-11 Front-end aplikacji StudyCave

This commit is contained in:
Marcin Szczepański 2019-11-20 19:29:07 +01:00
parent 6a5655cca9
commit 5fefcef27f
323 changed files with 18135 additions and 0 deletions

68
.angular-cli.json Normal file
View File

@ -0,0 +1,68 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "study-cave"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"./favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.min.css",
"../node_modules/primeicons/primeicons.css",
"../node_modules/primeng/resources/primeng.min.css",
"../node_modules/primeng/resources/themes/luna-amber/theme.css",
"../node_modules/primeng/resources/components/dialog/dialog.css",
"styles.css"
],
"scripts": [
"../node_modules/jquery/dist/jquery.min.js",
"../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

43
.gitignore vendored Normal file
View File

@ -0,0 +1,43 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
package-lock.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db

7
.htaccess Normal file
View File

@ -0,0 +1,7 @@
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !index
RewriteRule (.*) index.html [L]

18
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}\\serve",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}/dist/out-tsc/**/*.js"
]
}
]
}

14
e2e/app.e2e-spec.ts Normal file
View File

@ -0,0 +1,14 @@
import { AppPage } from './app.po';
describe('study-cave App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to app!');
});
});

11
e2e/app.po.ts Normal file
View File

@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('app-root h1')).getText();
}
}

14
e2e/tsconfig.e2e.json Normal file
View File

@ -0,0 +1,14 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

33
karma.conf.js Normal file
View File

@ -0,0 +1,33 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular/cli'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular/cli/plugins/karma')
],
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

68
package.json Normal file
View File

@ -0,0 +1,68 @@
{
"name": "study-cave",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.0.0",
"@angular/cdk": "^5.2.5",
"@angular/common": "^5.0.0",
"@angular/compiler": "^5.0.0",
"@angular/core": "^5.0.0",
"@angular/forms": "^5.0.0",
"@angular/http": "^5.0.0",
"@angular/material": "^5.2.5",
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/router": "^5.0.0",
"@auth0/angular-jwt": "^2.0.0",
"@fortawesome/fontawesome-svg-core": "^1.2.10",
"@fortawesome/free-solid-svg-icons": "^5.6.1",
"ag-grid-angular": "^19.1.2",
"ag-grid-community": "^19.1.4",
"ajv": "^6.0.0",
"angular-font-awesome": "^3.1.2",
"angular2-jwt": "^0.2.3",
"bootstrap": "^4.1.2",
"core-js": "^2.4.1",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"jquery-ui": "^1.12.1",
"picasso.js": "^0.18.2",
"popper.js": "^1.12.9",
"primeicons": "^1.0.0",
"primeng": "^6.1.7",
"rxjs": "^5.5.2",
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular-devkit/core": "^7.1.2",
"@angular/cli": "^1.7.4",
"@angular/compiler-cli": "^5.0.0",
"@angular/language-service": "^5.0.0",
"@types/jasmine": "~2.5.53",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.0.1",
"jasmine-core": "~2.5.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~3.2.0",
"tslint": "~5.7.0",
"typescript": "~2.4.2"
}
}

28
protractor.conf.js Normal file
View File

@ -0,0 +1,28 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@ -0,0 +1,108 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { FlashcardsModule } from './flashcards/flashcards.module';
import { TestsModule } from './tests/tests.module';
import { UserModule } from './user/user.module';
import { GroupsModule } from './groups/groups.module';
import { AuthGuard } from './auth-guard.service';
import { FlashcardsComponent } from './flashcards/flashcards/flashcards.component';
import { FlashcardsSetsListComponent } from './flashcards/flashcards-sets-list/flashcards-sets-list.component';
import { FlashcardsAddComponent } from './flashcards/flashcards-add/flashcards-add.component';
import { FlashcardsAddCsvComponent } from './flashcards/flashcards-add-csv/flashcards-add-csv.component';
import { FlashcardsAddTableComponent } from './flashcards/flashcards-add-table/flashcards-add-table.component';
import { FlashcardsSetDetailComponent } from './flashcards/flashcards-set-detail/flashcards-set-detail.component';
import { FlashcardsPairsTestComponent } from './flashcards/flashcards-pairs-test/flashcards-pairs-test.component';
import { FlashcardsMemoryTestComponent } from './flashcards/flashcards-memory-test/flashcards-memory-test.component';
import { FlashcardsEditTableComponent } from './flashcards/flashcards-edit-table/flashcards-edit-table.component';
import { FlashcardsFillingInTestComponent } from './flashcards/flashcards-filling-in-test/flashcards-filling-in-test.component';
import { FlashcardsTyperaceTestComponent } from './flashcards/flashcards-typerace-test/flashcards-typerace-test.component';
import { HomePageComponent } from './home-page/home-page.component';
import { LoginComponent } from './login/login.component';
import { TestMakerComponent } from './tests/test-maker/test-maker.component';
import { TestEditComponent } from './tests/test-edit/test-edit.component';
import { RegisterComponent } from './user/register/register.component';
import { UserComponent } from './user/user/user.component';
import { EditUserComponent } from './user/edit-user/edit-user.component';
import { MaterialsMenuComponent } from './materials/materials-menu/materials-menu.component';
import { MaterialsListComponent } from './materials/materials-list/materials-list.component';
import { MaterialsAddComponent } from './materials/materials-add/materials-add.component';
import { TestsListComponent } from './tests/tests-list/tests-list.component';
import { WorkInProgressComponent } from './work-in-progress/work-in-progress.component';
import { TestDetailsComponent } from './tests/test-details/test-details.component';
import { MaterialsDetailsComponent } from './materials/materials-details/materials-details.component';
import { MyGroupsComponent } from './groups/my-groups/my-groups.component';
import { GroupCreatorComponent } from './groups/group-creator/group-creator.component';
import { JoinToGroupComponent } from './groups/join-to-group/join-to-group.component';
import { GroupDetailsComponent } from './groups/group-details/group-details.component';
import { ManageGroupComponent } from './groups/manage-group/manage-group.component';
import { SharingResourcesInGroupsComponent } from './groups/sharing-resources-in-groups/sharing-resources-in-groups.component';
import { CommentsComponent } from './shared/comments/comments.component';
import { SharedModule } from './shared/shared.module';
import { WaitingResourcesComponent } from './groups/waiting-resources/waiting-resources.component';
import { BagdesComponent } from './user/bagdes/bagdes.component';
import { RankingComponent } from './groups/ranking/ranking.component';
import { HistoryOfActivityInGroupComponent } from './groups/history-of-activity-in-group/history-of-activity-in-group.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: '', component: HomePageComponent },
{ path: 'home', component: HomePageComponent },
{ path: 'flashcards', component: FlashcardsComponent },
{ path: 'flashcards/sets', component: FlashcardsSetsListComponent },
{ path: 'flashcards/add', component: FlashcardsAddComponent, canActivate: [AuthGuard] },
{ path: 'flashcards/add/table', component: FlashcardsAddTableComponent , canActivate: [AuthGuard]},
{ path: 'flashcards/add/csv', component: FlashcardsAddCsvComponent, canActivate: [AuthGuard] },
{ path: 'flashcards/sets/:id', component: FlashcardsSetDetailComponent },
{ path: 'flashcards/test-gen/flashcards-pairs/:id', component: FlashcardsPairsTestComponent },
{ path: 'flashcards/test-gen/flashcards-memory/:id', component: FlashcardsMemoryTestComponent },
{ path: 'flashcards/test-gen/flashcards-typerace/:id', component: FlashcardsTyperaceTestComponent },
{ path: 'flashcards/test-gen/flashcards-filling-in/:id', component: FlashcardsFillingInTestComponent },
{ path: 'flashcards/sets/edit/:id', component: FlashcardsEditTableComponent, canActivate: [AuthGuard] },
{ path: 'tests', component: TestsListComponent},
{ path: 'tests/:id', component: TestDetailsComponent},
{ path: 'tests/edit/:id', component: TestEditComponent, canActivate: [AuthGuard]},
{ path: 'test-maker', component: TestMakerComponent, canActivate: [AuthGuard] },
{ path: 'sign-up', component: RegisterComponent },
{ path: 'profile/:id', component: UserComponent, canActivate: [AuthGuard] },
{ path: 'edit-profile', component: EditUserComponent, canActivate: [AuthGuard] },
{ path: 'materials', component: MaterialsMenuComponent },
{ path: 'materials/list', component: MaterialsListComponent },
{ path: 'materials/add-materials', component: MaterialsAddComponent, canActivate: [AuthGuard] },
{ path: 'work-in-progress', component: WorkInProgressComponent },
{ path: 'materials/:id', component: MaterialsDetailsComponent},
{ path: 'my-groups', component: MyGroupsComponent, canActivate: [AuthGuard] },
{ path: 'create-group', component: GroupCreatorComponent, canActivate: [AuthGuard] },
{ path: 'join-to-group', component: JoinToGroupComponent, canActivate: [AuthGuard] },
{ path: 'groups/:id', component: GroupDetailsComponent, canActivate: [AuthGuard] },
{ path: 'groups/manage/:id', component: ManageGroupComponent, canActivate: [AuthGuard] },
{ path: 'groups/add-resources/:id', component: SharingResourcesInGroupsComponent , canActivate: [AuthGuard] },
{ path: 'groups/waiting-resources/:id', component: WaitingResourcesComponent, canActivate: [AuthGuard] },
{ path: 'badges', component: BagdesComponent, canActivate: [AuthGuard] },
{ path: 'groups/ranking/:id', component: RankingComponent , canActivate: [AuthGuard] },
{ path: 'groups/waiting-resources/:id', component: WaitingResourcesComponent, canActivate: [AuthGuard] },
{ path: 'groups/waiting-resources/:id', component: WaitingResourcesComponent, canActivate: [AuthGuard] },
{ path: 'groups/history/:id', component: HistoryOfActivityInGroupComponent, canActivate: [AuthGuard] }
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload', useHash: false}),
FlashcardsModule,
TestsModule,
UserModule,
GroupsModule,
SharedModule
],
exports: [
RouterModule,
FlashcardsModule,
TestsModule,
UserModule,
SharedModule
]
})
export class AppRoutingModule { }

30
src/app/app.component.css Normal file
View File

@ -0,0 +1,30 @@
.content{
width: 100%;
position: fixed;
margin-top: 100px;
max-width: 100%;
overflow: auto;
height: calc(100vh - 154px);
min-height: calc(100vh - 154px);
}
.wrapper{
overflow: auto;
}
@media screen and (max-width: 800px) {
.content{
margin-top: 170px;
height: calc(100vh - 200px);
min-height: calc(100vh - 200px);
}
}
@media screen and (max-width: 352px) {
.content{
margin-top: 170px;
height: calc(100vh - 250px);
min-height: calc(100vh - 250px);
}
}

View File

@ -0,0 +1,8 @@
<div class="wrapper">
<app-main-navigation></app-main-navigation>
<div class="content">
<router-outlet></router-outlet>
<br /><br /><br /><br />
</div>
<app-footer></app-footer>
</div>

View File

@ -0,0 +1,31 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { RoutingStateService } from './routing-state.service';
import { RouterTestingModule } from '@angular/router/testing';
describe('AppComponent', () => {
let component: AppComponent;
let fixture: ComponentFixture<AppComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
providers: [RoutingStateService],
imports: [RouterTestingModule],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create the app', async(() => {
expect(component).toBeTruthy();
}));
});

17
src/app/app.component.ts Normal file
View File

@ -0,0 +1,17 @@
import { Component, OnInit, Input } from '@angular/core';
import { RoutingStateService } from './routing-state.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app';
constructor(routingState: RoutingStateService) {
routingState.loadRouting();
}
ngOnInit() {}
}

60
src/app/app.module.ts Normal file
View File

@ -0,0 +1,60 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { MaterialsModule } from './materials/materials.module';
import { FlashcardsModule } from './flashcards/flashcards.module';
import { TestsModule } from './tests/tests.module';
import { GroupsModule } from './groups/groups.module';
import { UserModule } from './user/user.module';
import { httpInterceptorProviders } from './http-interceptors/index';
import { LocationStrategy, HashLocationStrategy, PathLocationStrategy } from '@angular/common';
import { LoginComponent } from './login/login.component';
import { AppComponent } from './app.component';
import { MainNavigationComponent } from './main-navigation/main-navigation.component';
import { FooterComponent } from './footer/footer.component';
import { HomePageComponent } from './home-page/home-page.component';
import { WorkInProgressComponent } from './work-in-progress/work-in-progress.component';
import { AuthGuard } from './auth-guard.service';
import { AuthenticationService } from './authentication.service';
import { SharedModule } from './shared/shared.module';
import { RoutingStateService } from './routing-state.service';
import { AutofocusDirective } from './autofocus.directive';
@NgModule({
declarations: [
LoginComponent,
AppComponent,
MainNavigationComponent,
FooterComponent,
HomePageComponent,
WorkInProgressComponent,
AutofocusDirective
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
HttpClientModule,
AppRoutingModule,
FlashcardsModule,
MaterialsModule,
TestsModule,
GroupsModule,
UserModule,
SharedModule
],
providers: [AuthGuard, httpInterceptorProviders, AuthenticationService, RoutingStateService, {
provide: LocationStrategy, useClass: PathLocationStrategy
}],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -0,0 +1,16 @@
import { TestBed, inject } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AuthGuard } from './auth-guard.service';
describe('AuthGuardService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthGuard],
imports: [RouterTestingModule]
});
});
it('should be created', inject([AuthGuard], (service: AuthGuard) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,19 @@
import {Injectable} from '@angular/core';
import {Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (localStorage.getItem('currentUser')) {
// logged in so return true
return true;
}
// tslint:disable-next-line:one-line
else {
this.router.navigate(['/login']);
return false;
}
}
}

View File

@ -0,0 +1,19 @@
import { TestBed, inject } from '@angular/core/testing';
import { AuthenticationService } from './authentication.service';
import { HttpClientModule } from '@angular/common/http';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('AuthenticationService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthenticationService],
imports: [HttpClientModule, MatSnackBarModule],
schemas: [NO_ERRORS_SCHEMA]
});
});
it('should be created', inject([AuthenticationService], (service: AuthenticationService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,71 @@
import { Injectable, Output, EventEmitter } from '@angular/core';
import { HttpClient, HttpHeaders, HttpRequest, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class AuthenticationService {
@Output() getLoggedInName: EventEmitter<any> = new EventEmitter();
private headers = new HttpHeaders({ 'Content-Type': 'application/json' });
private storageSub = new Subject<boolean>();
public token: string;
constructor(private http: HttpClient, public snackBar: MatSnackBar) {
}
watchStorage(): Observable<any> {
return this.storageSub.asObservable();
}
watchStorageChanges(): void {
this.storageSub.next(true);
}
login(username: string, password: string): Observable<boolean> {
return this.http.post('login', { password: password, username: username }, { headers: this.headers, observe: 'response' })
.map((response) => {
// czy login ok jeśli w response jest token
// const token = response.json() && response.json().token;
const token = response.headers.get('authorization');
if (token) {
// store username and jwt token w local storage aby nie wylogowało przy zmianie stron
localStorage.setItem('currentUser', JSON.stringify({ username: username, authorization: token }));
this.getLoggedInName.emit('logged');
this.storageSub.next(true);
this.snackBar.open('Zalogowano pomyślnie!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-success'] });
// return true jeśli ok
return true;
} else {
// return false jeśli nie
this.getLoggedInName.emit('notLogged');
return false;
}
}).catch((error: any) => Observable.throw(error.json().error || 'Server error'));
}
getToken(): String {
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
if (currentUser.authorization == null) {
return ' ';
} else {
return currentUser.authorization;
}
}
isLoggedIn(): boolean {
const token: String = this.getToken();
return token && token.length > 0;
}
logout(): void {
// clear token remove user from local storage to log user out
this.snackBar.open('Wylogowano pomyślnie!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-success'] });
localStorage.removeItem('currentUser');
}
}

View File

@ -0,0 +1,7 @@
import { AutofocusDirective } from './autofocus.directive';
describe('AutofocusDirective', () => {
it('should create an instance', () => {
});
});

View File

@ -0,0 +1,15 @@
import { Directive, AfterViewInit, ElementRef } from '@angular/core';
@Directive({
selector: '[appAutofocus]'
})
export class AutofocusDirective implements AfterViewInit {
constructor(private el: ElementRef) {
}
ngAfterViewInit() {
this.el.nativeElement.focus();
}
}

View File

@ -0,0 +1,8 @@
import { FilterUserPipe } from './filter-user.pipe';
describe('FilterUserPipe', () => {
it('create an instance', () => {
const pipe = new FilterUserPipe();
expect(pipe).toBeTruthy();
});
});

View File

@ -0,0 +1,20 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filterUser'
})
export class FilterUserPipe implements PipeTransform {
transform(sets: any, searchOwner: any ): any {
if (searchOwner === undefined || sets === undefined) {
return sets;
} else {
return sets.filter(function(set){
return set.owner.toString() === (searchOwner.toString());
});
}
}
}

View File

@ -0,0 +1,8 @@
import { FilterPipe } from './filter.pipe';
describe('FilterPipe', () => {
it('create an instance', () => {
const pipe = new FilterPipe();
expect(pipe).toBeTruthy();
});
});

20
src/app/filter.pipe.ts Normal file
View File

@ -0,0 +1,20 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform {
transform(sets: any, search: any ): any {
if (search === undefined || sets === undefined) {
return sets;
} else {
return sets.filter(function(set){
return set.permission === (search);
});
}
}
}

View File

@ -0,0 +1,55 @@
.description {
background-color: #22272a;
padding: 1rem;
margin-top: 1rem;
margin-bottom: 2rem;
text-align: left;
}
.container {
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
}
.wrapper-description {
display: flex;
justify-content: center;
align-content: center;
}
.wrapper-add {
width: 100%;
display: flex;
justify-content: center;
align-content: center;
margin-bottom: 4rem;
}
.wrapper{
width: 100%;
padding: 30px;
min-height: 100%;
margin-bottom: 40px;
}
.content{
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
margin-left: auto;
margin-right: auto;
}
ag-grid-angular{
margin: 10px;
}
.buttons-container > div{
margin: 10px;
}

View File

@ -0,0 +1,53 @@
<div class="wrapper">
<div class="content">
<div class="buttons-container">
<button class="btn btn-study-cave" routerLink="/flashcards/sets"><i class="fas fa-arrow-left"></i> WRÓĆ DO LISTY</button>
<button class="btn btn-study-cave" routerLink="/flashcards/add/table"><i class="fas fa-table"></i> DODAJ ZA POMOCĄ TABELI</button>
</div>
</div>
<div class="wrapper-add">
<div class="container">
<h1>Importowanie zestawu z pliku CSV</h1>
<br />
<h3>Twój plik CSV powinien mieć poniższy format:</h3>
<div class="wrapper-description">
<div class="description row">
<h4 class="col-8 ml-4">
nazwa zestawu;kategoria <br />
lewa strona fiszki;prawa strona fiszki <br />
lewa strona fiszki;prawa strona fiszki <br />
lewa strona fiszki;prawa strona fiszki <br />
.....
</h4>
<p class="col-3 ml-4">
<u>Przykład:</u> <br />
zwierzęta;angielski <br />
pies;dog <br />
kot;cat <br />
królik;rabbit <br />
ryba;fish <br />
</p>
</div>
</div>
<div *ngIf="currentFileUpload" class="progress">
<div class="progress-bar progress-bar-info progress-bar-striped"
role="progressbar" attr.aria-valuenow="{{progress.percentage}}"
aria-valuemin="0" aria-valuemax="100"
[ngStyle]="{width:progress.percentage+'%'}">
{{progress.percentage}}%</div>
</div>
<label>
<p>Wybierz plik:</p>
<input type="file" accept=".csv" (change)="selectFile($event)">
</label>
<div>
<br />
<input type="checkbox" name="{{ permission }}" class="form-control" [checked]="permission" (change)="changePermission()"/><b>Udostępnij publicznie</b>
</div><br />
<button class="btn btn-study-cave" [disabled]="!selectedFiles" (click)="upload()"><i class="fas fa-check-circle"></i> Dodaj fiszki!</button>
<br /><br />
<button class="btn btn-study-cave" routerLink="/flashcards/sets/"><i class="fas fa-arrow-left"></i> Powrót</button>
</div>
</div>
</div>

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsAddCsvComponent } from './flashcards-add-csv.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { HttpClientModule } from '@angular/common/http';
import { RouterTestingModule } from '@angular/router/testing';
import { AuthenticationService } from '../../authentication.service';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsAddCsvComponent', () => {
let component: FlashcardsAddCsvComponent;
let fixture: ComponentFixture<FlashcardsAddCsvComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsAddCsvComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [HttpClientModule, RouterTestingModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsAddCsvComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,75 @@
import { Component, OnInit } from '@angular/core';
import { HttpClient, HttpResponse, HttpEventType } from '@angular/common/http';
import { FlashcardsService } from '../flashcards.service';
import { Router } from '@angular/router';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-flashcards-add-csv',
templateUrl: './flashcards-add-csv.component.html',
styleUrls: ['./flashcards-add-csv.component.css']
})
export class FlashcardsAddCsvComponent implements OnInit {
selectedFiles: FileList;
currentFileUpload: File;
progress: { percentage: number } = { percentage: 0 };
currentUser = JSON.parse(localStorage.getItem('currentUser'));
user: string;
permission: Boolean = true;
constructor(private uploadService: FlashcardsService, private router: Router, public snackBar: MatSnackBar) { }
ngOnInit() { this.isLoggedIn(); }
isLoggedIn() {
if (localStorage.getItem('currentUser') === null) {
this.user = 'Anonim';
} else {
this.user = this.currentUser.username;
}
}
changePermission(): void {
this.permission = !this.permission;
}
selectFile(event) {
this.selectedFiles = event.target.files;
}
upload() {
this.progress.percentage = 0;
const url = 'file/upload';
let p = 'Private';
if (this.permission) {
p = 'Public';
}
this.currentFileUpload = this.selectedFiles.item(0);
if (this.currentFileUpload.type === 'application/vnd.ms-excel') {
this.uploadService.pushFileToStorage(this.currentFileUpload, this.user, p, url).subscribe(
event => {
if (event.type === HttpEventType.UploadProgress) {
this.progress.percentage = Math.round(100 * event.loaded / event.total);
} else if (event instanceof HttpResponse) {
this.currentFileUpload = undefined;
this.snackBar.open(`Plik został zaimportowany.
Swoje fiszki możesz podejrzeć na liście zestawów fiszek
i tam je edytować jeśli zajdzie taka potrzeba :)`, null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-success'] });
this.router.navigate(['flashcards/sets']);
}
},
error => {
this.snackBar.open('Coś poszło nie tak. Spróbuj ponownie później.', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
this.currentFileUpload = undefined;
}
);
} else {
this.currentFileUpload = undefined;
this.snackBar.open('Wybierz plik CSV!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
}
this.selectedFiles = undefined;
}
}

View File

@ -0,0 +1,49 @@
label {
display: block;
}
table, th, td, tr {
border-width: 0;
background-color: transparent;
}
.container {
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
}
.wrapper-add {
width: 100%;
display: flex;
justify-content: center;
align-content: center;
margin-bottom: 4rem;
}
.wrapper{
width: 100%;
padding: 30px;
min-height: 100%;
margin-bottom: 40px;
}
.content{
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
margin-left: auto;
margin-right: auto;
}
ag-grid-angular{
margin: 10px;
}
.buttons-container > div{
margin: 10px;
}

View File

@ -0,0 +1,68 @@
<div class="wrapper">
<div class="content">
<div class="buttons-container">
<button class="btn btn-study-cave" routerLink="/flashcards/sets"><i class="fas fa-arrow-left"></i> WRÓĆ DO LISTY</button>
<button class="btn btn-study-cave" routerLink="/flashcards/add/csv"><i class="fas fa-file-csv"></i> DODAJ ZA POMOCĄ CSV</button>
</div>
</div>
<div class="wrapper-add">
<div class="container">
<h1>Tworzenie zestawu</h1>
<br />
<form ngNativeValidate action="/" method="post" #user="ngForm" (ngSubmit)="addTable(user.value)">
<label>
Wprowadź nazwę dla zestawu:
<input type="text" name="title" class="form-control" ngModel required />
</label>
<label>
Wprowadź kategorię zestawu:
<input type="text" name="category" class="form-control" ngModel required />
</label>
<br />
<label>
Wypełnij tabelę fiszkami:
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Lewa strona</th>
<th>Prawa strona</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let field of fieldArray; let i = index">
<td>
<input [(ngModel)]="field.left_side" class="form-control" type="text" name="{{field.left_side}}" />
</td>
<td>
<input [(ngModel)]="field.right_side" class="form-control" type="text" name="{{field.right_side}}" />
</td>
<td>
<button class="btn btn-study-cave" type="button" (click)="deleteFieldValue(i)"><i class="fas fa-trash-alt"></i> Usuń fiszkę</button>
</td>
</tr>
<tr>
<td>
<input class="form-control" type="text" id="newAttributeLeft" [(ngModel)]="newAttribute.left_side" name="newAttributeLeft" />
</td>
<td>
<input class="form-control" type="text" id="newAttributeRight" [(ngModel)]="newAttribute.right_side" name="newAttributeRight" />
</td>
<td>
<button class="btn btn-study-cave" type="button" (click)="addFieldValue()"><i class="fas fa-plus"></i> Dodaj fiszkę</button>
</td>
</tr>
</tbody>
</table>
</label>
<div>
<br />
<input type="checkbox" name="{{ permission }}" class="form-control" [checked]="permission" (change)="changePermission()"/>
<b>Udostępnij publicznie</b>
</div>
<br /><br />
<button class="btn btn-study-cave" type="submit"><i class="fas fa-check-circle"></i> Dodaj fiszki!</button>
<button class="btn btn-study-cave" routerLink="/flashcards/sets/"><i class="fas fa-arrow-left"></i> Powrót</button>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,44 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsAddTableComponent } from './flashcards-add-table.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { HttpClientModule } from '@angular/common/http';
import { RouterTestingModule } from '@angular/router/testing';
import { AuthenticationService } from '../../authentication.service';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { FormsModule } from '@angular/forms';
describe('FlashcardsAddTableComponent', () => {
let component: FlashcardsAddTableComponent;
let fixture: ComponentFixture<FlashcardsAddTableComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsAddTableComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [HttpClientModule, RouterTestingModule, MatSnackBarModule, FormsModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsAddTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
// Testowanie funkcji w klasie
it('should change permissions', () => {
expect(component.permission).toBe(true);
component.changePermission();
expect(component.permission).toBe(false);
component.changePermission();
expect(component.permission).toBe(true);
});
});

View File

@ -0,0 +1,84 @@
import { Component, OnInit } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-flashcards-add-table',
templateUrl: './flashcards-add-table.component.html',
styleUrls: ['./flashcards-add-table.component.css']
})
export class FlashcardsAddTableComponent implements OnInit {
table: Boolean = false;
tableToSend: any = {};
public fieldArray: Array<any> = [];
newAttribute: any = {};
currentUser;
permission: Boolean = true;
constructor(private flashcardsService: FlashcardsService, public snackBar: MatSnackBar) { }
ngOnInit() { this.isLoggedIn(); }
changePermission(): void {
this.permission = !this.permission;
}
isLoggedIn() {
if (localStorage.getItem('currentUser') === null) {
this.currentUser = 'Anonim';
} else {
this.currentUser = JSON.parse(localStorage.getItem('currentUser'));
}
}
addFieldValue() {
const undefinedAttr = ((this.newAttribute['left_side'] === undefined) || (this.newAttribute['right_side'] === undefined));
if (undefinedAttr) {
this.snackBar.open('Nie można dodać fiszki z pustym polem!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
} else {
const length = ((this.newAttribute['left_side'].trim().length === 0) || (this.newAttribute['right_side'].trim().length === 0));
if (length) {
this.snackBar.open('Nie można dodać fiszki z pustym polem!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
} else {
this.fieldArray.push(this.newAttribute);
this.newAttribute = {};
}
}
}
deleteFieldValue(index) {
this.fieldArray.splice(index, 1);
}
setOwner(): string {
if (localStorage.getItem('currentUser')) {
return JSON.parse(localStorage.getItem('currentUser')).username;
} else {
return 'anonim';
}
}
addTable(value: any) {
// obsługa formularza dodawania fiszek do tabeli
if (this.fieldArray.length === 0) {
this.snackBar.open('Zestaw fiszek nie może być pusty!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
} else {
let p = 'Private';
if (this.permission) {
p = 'Public';
}
this.tableToSend = {
name: value.title,
category: value.category,
owner: this.setOwner(),
flashcards: this.fieldArray,
permission: p
};
this.flashcardsService.add(this.tableToSend);
}
}
}

View File

@ -0,0 +1,36 @@
.wrapper{
width: 100%;
padding: 30px;
min-height: 100%;
margin-bottom: 40px;
}
@media screen and (max-width: 800px) {
.mobile{
display: none;
}
}
.empty{
text-align: center;
}
.content{
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
margin-left: auto;
margin-right: auto;
}
ag-grid-angular{
margin: 10px;
}
.buttons-container > div{
margin: 10px;
}

View File

@ -0,0 +1,9 @@
<div class="wrapper">
<div class="content">
<div class="buttons-container">
<button class="btn btn-study-cave" routerLink="/flashcards/sets"><i class="fas fa-arrow-left"></i> WRÓĆ DO LISTY</button>
<button class="btn btn-study-cave" routerLink="/flashcards/add/table"><i class="fas fa-table"></i> DODAJ ZA POMOCĄ TABELI</button>
<button class="btn btn-study-cave" routerLink="/flashcards/add/csv"><i class="fas fa-file-csv"></i> DODAJ ZA POMOCĄ CSV</button>
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsAddComponent } from './flashcards-add.component';
describe('FlashcardsAddComponent', () => {
let component: FlashcardsAddComponent;
let fixture: ComponentFixture<FlashcardsAddComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsAddComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsAddComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-flashcards-add',
templateUrl: './flashcards-add.component.html',
styleUrls: ['./flashcards-add.component.css']
})
export class FlashcardsAddComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,49 @@
label {
display: block;
}
table, th, td, tr {
border-width: 0;
background-color: transparent;
}
.container {
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
}
.wrapper-add {
width: 100%;
display: flex;
justify-content: center;
align-content: center;
margin-bottom: 4rem;
}
.wrapper{
width: 100%;
padding: 30px;
min-height: 100%;
margin-bottom: 40px;
}
.content{
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
margin-left: auto;
margin-right: auto;
}
ag-grid-angular{
margin: 10px;
}
.buttons-container > div{
margin: 10px;
}

View File

@ -0,0 +1,73 @@
<div class="wrapper">
<div class="content">
<div class="buttons-container">
<button class="btn btn-study-cave" (click)="returnToSet()"><i class="fas fa-arrow-left"></i> WRÓĆ DO ZESTAWU</button>
</div>
</div>
<div class="wrapper-add">
<div class="container">
<h1>Edycja zestawu</h1>
<br />
<form ngNativeValidate action="/" method="post" #user="ngForm" (ngSubmit)="addTable(user.value)">
<div class="row">
<div class="col-md-10">
<label>
Wprowadź nazwę dla zestawu:
<input type="text" name="title" class="form-control" [(ngModel)]="set['name']" required />
</label>
<label>
Wprowadź kategorię zestawu:
<input type="text" name="category" class="form-control" [(ngModel)]="set['category']" required />
</label>
</div>
<label class="col-md-2">
<br />
<input type="checkbox" name="{{ permission }}" class="form-control" [checked]="permission" (change)="changePermission()"/>
<b>Udostępnij publicznie</b>
</label>
</div>
<br />
<label>
Wypełnij tabelę fiszkami:
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Lewa strona</th>
<th>Prawa strona</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let field of fieldArray; let i = index">
<td>
<input [(ngModel)]="field.left_side" class="form-control" type="text" name="{{field.left_side}}" />
</td>
<td>
<input [(ngModel)]="field.right_side" class="form-control" type="text" name="{{field.right_side}}" />
</td>
<td>
<button class="btn btn-study-cave" type="button" (click)="deleteFieldValue(i)"><i class="fas fa-trash-alt"></i> Usuń fiszkę</button>
</td>
</tr>
<tr>
<td>
<input class="form-control" type="text" id="newAttributeLeft" [(ngModel)]="newAttribute['left_side']" name="newAttributeLeft" />
</td>
<td>
<input class="form-control" type="text" id="newAttributeRight" [(ngModel)]="newAttribute['right_side']" name="newAttributeRight" />
</td>
<td>
<button class="btn btn-study-cave" type="button" (click)="addFieldValue()"><i class="fas fa-file-csv"></i> Dodaj fiszkę</button>
</td>
</tr>
</tbody>
</table>
</label>
<br />
<div>
<button class="btn btn-study-cave" type="submit"><i class="fas fa-check-circle"></i> Edytuj fiszki!</button>
<button class="btn btn-study-cave" [routerLink]="['/flashcards/sets', ident]"><i class="fas fa-arrow-left"></i> Powrót</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,35 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsEditTableComponent } from './flashcards-edit-table.component';
import { FormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { HttpClientModule } from '@angular/common/http';
import { AuthenticationService } from '../../authentication.service';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsEditTableComponent', () => {
let component: FlashcardsEditTableComponent;
let fixture: ComponentFixture<FlashcardsEditTableComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsEditTableComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsEditTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,106 @@
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { Subscription } from 'rxjs/Subscription';
import { ActivatedRoute } from '@angular/router';
import { Router } from '@angular/router';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-flashcards-edit-table',
templateUrl: './flashcards-edit-table.component.html',
styleUrls: ['./flashcards-edit-table.component.css']
})
export class FlashcardsEditTableComponent implements OnInit, OnDestroy {
ident: number;
permission: Boolean = false;
table: Boolean = false;
tableToSend: any = {};
fieldArray: Array<any> = [];
newAttribute: any = {};
set: Object = {};
flashcardSubscribtion: Subscription;
constructor(private flashcardsService: FlashcardsService, private route: ActivatedRoute, private router: Router,
public snackBar: MatSnackBar) { }
ngOnInit() {
this.ident = this.route.snapshot.params.id;
this.flashcardSubscribtion = this.flashcardsService.getSet(this.ident).subscribe(data => {
this.set = data;
if (data['permission'] === 'Private') {
this.permission = false;
} else {
this.permission = true;
}
const flashcards = data['flashcards'];
for (let i = 0; i < flashcards.length; i++) {
const id = flashcards[i]['id'];
this.fieldArray.push({
id: id,
left_side: flashcards[i]['left_side'],
right_side: flashcards[i]['right_side']
});
}
});
}
returnToSet() {
this.router.navigate(['/flashcards/sets/', this.ident]);
}
addFieldValue() {
const undefinedAttr = ((this.newAttribute['left_side'] === undefined) || (this.newAttribute['right_side'] === undefined));
if (undefinedAttr) {
this.snackBar.open('Nie można dodać fiszki z pustym polem!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
} else {
const length = ((this.newAttribute['left_side'].trim().length === 0) || (this.newAttribute['right_side'].trim().length === 0));
if (length) {
this.snackBar.open('Nie można dodać fiszki z pustym polem!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
} else {
const insert = {
id: null,
left_side: this.newAttribute['left_side'],
right_side: this.newAttribute['right_side'],
};
this.fieldArray.push(insert);
this.newAttribute = {};
}
}
}
deleteFieldValue(index) {
this.fieldArray.splice(index, 1);
}
addTable(value: any) {
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
if (this.fieldArray.length === 0) {
this.snackBar.open('Zestaw fiszek nie może być pusty!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
} else {
let perm = 'Private';
if (this.permission) {
perm = 'Public';
}
this.tableToSend = {
id: this.ident,
name: value.title,
permission: perm,
category: value.category,
owner: currentUser.username,
flashcards: this.fieldArray
};
this.flashcardsService.edit(this.tableToSend);
}
}
changePermission(): void {
this.permission = !this.permission;
}
ngOnDestroy() {
this.flashcardSubscribtion.unsubscribe();
}
}

View File

@ -0,0 +1,43 @@
#container-small {
background-color: #181616;
padding: 30px;
width: 32%;
height: 400px;
box-sizing: border-box;
position: relative;
text-align: center;
}
.content {
display: flex;
justify-content: space-around;
align-content: center;
margin-top: 2rem;
}
#container-large {
background-color: #181616;
padding: 30px;
width: 62%;
min-height: 400px;
text-align: center;
}
.container {
margin-bottom: 4rem;
}
@media screen and (max-width: 800px) {
.content {
flex-wrap: wrap;
}
#container-small {
height: 250px;
}
#container-small, #container-large {
width: 90%;
margin-bottom: 2.5rem;
}
}

View File

@ -0,0 +1,59 @@
<div class="content">
<div id="container-small">
<h4>Uzupełnianie fiszek</h4>
<br />
<div>
<b>Nazwa zestawu: </b>
<i>{{ name }}</i>
<br />
<b>Kategoria: </b>
<i>{{ category }}</i>
</div>
<div *ngIf="started">
<div>
<div>
<b>Uzupełniono: </b>
<span> {{ filled }} z {{ length_test }} fiszek</span>
</div>
<div>
<b>Poprawnych odpowiedzi: </b>
<span> {{ good }} na {{ length_test }}</span><br />
<b>Złych odpowiedzi: </b>
<span> {{ bad }} na {{ length_test }}</span>
</div>
</div>
</div>
</div>
<div id="container-large">
<div *ngIf="started">
<div *ngIf="this.filled >= 1 && !finish">
Poprzednia odpowiedź:<br />
{{flashcards[index-1].content}} {{allAnswers[index-1].answer}}
</div>
<div *ngIf="this.filled < this.length_test && !finish ">
Podaj odpowiedź:<br />
{{flashcards[index].content}}
<input appAutofocus [(ngModel)]="answer" class="mr-2" placeholder="odpowiedź" (keyup.enter)="verifyAnswer()" >
<button class="btn btn-study-cave" (click)="verifyAnswer()"><i class="fas fa-check-circle"></i> Sprawdź</button>
</div>
<div *ngIf="this.is_correct === true && !finish">ODPOWIEDŹ POPRAWNA</div>
<div *ngIf="this.is_correct === false && !finish">ODPOWIEDŹ NIEPOPRAWNA</div>
<div *ngIf="this.filled < this.length_test-1 && !finish">
Następne pytanie:<br />
{{flashcards[index+1].content}}
</div>
</div>
<div *ngIf="finish">
<app-test-results [result]="good" [maxPts]="length_test"></app-test-results>
</div>
<div *ngIf="!finish">
<button class="btn btn-study-cave" *ngIf="!started" (click)="start()"><i class="far fa-play-circle"></i> Rozpocznij test</button>
</div>
<button class="btn btn-study-cave" *ngIf="finish" routerLink="/flashcards/sets/{{ id }}"><i class="far fa-stop-circle"></i> Zakończ test</button>
<button class="btn btn-study-cave" *ngIf="started && !finish && !not_last"(click)="finished()"><i class="fas fa-arrow-left"></i> Przejdź do wyniku</button>
<br />
<button class="btn btn-study-cave" routerLink="/flashcards/sets/{{ id }}"><i class="fas fa-arrow-left"></i> Wróć do zestawu</button>
</div>

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsFillingInTestComponent } from './flashcards-filling-in-test.component';
import { FormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { HttpClientModule } from '@angular/common/http';
import { AuthenticationService } from '../../authentication.service';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsFillingInTestComponent', () => {
let component: FlashcardsFillingInTestComponent;
let fixture: ComponentFixture<FlashcardsFillingInTestComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsFillingInTestComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsFillingInTestComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,96 @@
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { Subscription } from 'rxjs/Subscription';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-flashcards-filling-in-test',
templateUrl: './flashcards-filling-in-test.component.html',
styleUrls: ['./flashcards-filling-in-test.component.css']
})
export class FlashcardsFillingInTestComponent implements OnInit, OnDestroy {
id: number;
flashcardSubscribtionMeta: Subscription;
flashcardSubscribtion: Subscription;
flashcardSubscribtionCheck: Subscription;
name: String;
category: String;
length_test: number;
goodNow: number;
started: Boolean = false;
finish: Boolean = false;
flashcards: Array<Object> = [];
filled = 0;
good = 0;
bad = 0;
answer: String;
index = 0;
not_last: Boolean = true;
is_correct: Boolean;
allAnswers = [];
constructor(private flashcardsService: FlashcardsService, private route: ActivatedRoute) { }
ngOnInit() {
this.id = this.route.snapshot.params.id;
this.flashcardSubscribtionMeta = this.flashcardsService.getSet(this.id).subscribe(data => {
this.name = data['name'];
this.category = data['category'];
});
this.flashcardSubscribtion = this.flashcardsService.getTestFilling(this.id).subscribe(data => {
this.length_test = data.length;
this.flashcards = data;
});
}
start() {
this.started = true;
}
finished() {
this.finish = true;
}
verifyAnswer() {
const body = [];
const n = this.length_test;
if (this.answer === '') {
this.answer = ' ';
}
this.allAnswers.push({
id: this.index,
answer: this.answer,
});
body.push({
id: this.flashcards[this.index]['id'],
content: this.answer,
side: this.flashcards[this.index]['side'],
});
this.flashcardSubscribtionCheck = this.flashcardsService.testCheck(this.id, body[0])
.subscribe(data => {
this.is_correct = data.result;
if (this.is_correct === true) {
this.good = this.good + 1;
} else {
this.bad = this.bad + 1;
}
});
if (this.index < this.length_test) {
this.index = this.index + 1;
this.filled = this.filled + 1;
this.answer = '';
}
if (this.index === this.length_test) {
this.not_last = false;
}
}
ngOnDestroy() {
this.flashcardSubscribtionMeta.unsubscribe();
this.flashcardSubscribtion.unsubscribe();
if (this.flashcardSubscribtionCheck) {
this.flashcardSubscribtionCheck.unsubscribe();
}
}
}

View File

@ -0,0 +1,26 @@
h2 {
text-align: center;
font-weight: 900;
}
.clickable {
cursor: pointer;
}
.card {
color: white;
background-color: gray;
text-align: center;
min-height: 4rem;
display: flex;
justify-content: center;
transition: color 1000ms;
}
.card-invisible {
font-size: 2.5rem;
}
.card-visible {
font-size: 1.5rem;
}

View File

@ -0,0 +1,14 @@
<div class="container">
<div>
<h2 *ngIf="!isOK && !isBad">&nbsp;</h2>
<h2 *ngIf="isOK">Dobrze :)</h2>
<h2 *ngIf="isBad">Źle :(</h2>
</div>
<br />
<div class="row">
<div *ngFor="let card of set; let i = index" class="col-md-6">
<p *ngIf="!visible[i]" id="{{ i }}" class="card card-invisible clickable" (click)="check($event)">???</p>
<p *ngIf="visible[i]" id="{{ i }}" class='card card-visible unclickable' onclick='return false;'><b>{{ card }}</b></p>
</div>
</div>
</div>

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsMemoryTestSetComponent } from './flashcards-memory-test-set.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { AuthenticationService } from '../../authentication.service';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsMemoryTestSetComponent', () => {
let component: FlashcardsMemoryTestSetComponent;
let fixture: ComponentFixture<FlashcardsMemoryTestSetComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsMemoryTestSetComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsMemoryTestSetComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,121 @@
import { Component, OnInit, OnChanges, SimpleChanges, Input, Output, EventEmitter, OnDestroy } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { Subscription } from 'rxjs/Subscription';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-flashcards-memory-test-set',
templateUrl: './flashcards-memory-test-set.component.html',
styleUrls: ['./flashcards-memory-test-set.component.css']
})
export class FlashcardsMemoryTestSetComponent implements OnInit, OnChanges, OnDestroy {
@Input() package: Array<any>;
@Input() id: number;
@Input() package_id: number;
@Output() goodEvent = new EventEmitter();
@Output() isChecked = new EventEmitter();
flashcardSubscribtion: Array<Subscription> = [];
answer: Array<Object> = [];
set: Array<Object> = [];
visible: Array<Boolean> = [];
toCheck: Array<number> = [];
isBad: Boolean = false;
isOK: Boolean = false;
good = 0;
clicks = 0;
constructor(private uploadService: FlashcardsService, public snackBar: MatSnackBar) { }
ngOnInit() {
this.isChecked.emit(false);
this.flashcardSubscribtion = [];
this.visible = [];
this.set = this.package[this.package_id]['set'];
for (let i = 0; i < this.set.length; i++) {
this.visible.push(false);
}
}
ngOnChanges(changes: SimpleChanges) {
this.ngOnDestroy();
const package_idChanges = changes['package_id'];
if (package_idChanges) {
this.ngOnInit();
}
}
check(event) {
if (this.clicks <= 2) {
this.clicks += 1;
this.toCheck.push(event.target.id);
this.visible[this.toCheck[0]] = true;
if (this.clicks === 2) {
this.visible[this.toCheck[1]] = true;
const toSend = {
x: this.set[this.toCheck[0]],
y: this.set[this.toCheck[1]],
};
setTimeout(() => {
this.flashcardSubscribtion[this.flashcardSubscribtion.length] =
this.uploadService.testMemory(this.id, toSend).subscribe(data => {
this.showWrong(data);
this.clicks = 0;
},
error => {
this.snackBar.open('Coś poszło nie tak :( Spróbuj ponownie później.', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
this.clicks = 0;
}
);
}, 1000);
}
}
}
showWrong(answer: Boolean) {
this.isGood(answer);
this.goodEvent.emit(this.good);
}
isGood(answer: Boolean) {
if (!answer) {
this.isBad = true;
this.visible[this.toCheck[0]] = false;
this.visible[this.toCheck[1]] = false;
setTimeout(() => {
this.isBad = false;
}, 1000);
} else {
this.isOK = true;
this.visible[this.toCheck[0]] = true;
this.visible[this.toCheck[1]] = true;
document.getElementById(this.toCheck[0].toString()).style.color = 'rgb(94, 249, 37)';
document.getElementById(this.toCheck[1].toString()).style.color = 'rgb(94, 249, 37)';
setTimeout(() => {
this.isOK = false;
}, 1000);
this.good += 1;
let i = 0;
while (this.visible[i]) {
if (i === (this.visible.length - 1)) {
this.isChecked.emit(true);
}
i++;
if (i === this.visible.length) {
break;
}
}
}
this.toCheck = [];
}
ngOnDestroy() {
for (let i = 0; i < this.flashcardSubscribtion.length; i++) {
this.flashcardSubscribtion[i].unsubscribe();
}
}
}

View File

@ -0,0 +1,43 @@
#container-small {
background-color: #181616;
padding: 30px;
width: 32%;
height: 400px;
box-sizing: border-box;
position: relative;
text-align: center;
}
.content {
display: flex;
justify-content: space-around;
align-content: center;
margin-top: 2rem;
}
#container-large {
background-color: #181616;
padding: 30px;
width: 62%;
min-height: 400px;
text-align: center;
}
.container {
margin-bottom: 4rem;
}
@media screen and (max-width: 800px) {
.content {
flex-wrap: wrap;
}
#container-small {
height: 250px;
}
#container-small, #container-large {
width: 90%;
margin-bottom: 2.5rem;
}
}

View File

@ -0,0 +1,49 @@
<div class="container">
<div class="content">
<div id="container-small">
<h4>Memory</h4>
<br />
<div>
<b>Nazwa zestawu: </b>
<i>{{ name }}</i>
<br />
<b>Kategoria: </b>
<i>{{ category }}</i>
</div>
<div *ngIf="started">
<div>
<div>
<b>Uzupełniono: </b>
<span> {{ filled }} z {{ flashcards }} fiszek</span>
</div>
<div>
<b>Poprawnych odpowiedzi: </b>
<span> {{ good }} na {{ flashcards }}</span>
</div>
</div>
</div>
</div>
<div id="container-large">
<div class="test" *ngIf="started">
<app-flashcards-memory-test-set (goodEvent)="goodEvent($event)" (isChecked)=isChecked($event)
[package]="packages" package_id="{{ package_id }}" [id]="id"></app-flashcards-memory-test-set>
<br />
<div>
<b>Ekran: </b>
<span> {{ package_id + 1 }} z {{ length_packages }}</span>
</div>
</div>
<div *ngIf="finish">
<app-test-results [result]="good" [maxPts]="flashcards"></app-test-results>
</div>
<br />
<div *ngIf="!finish">
<button class="btn btn-study-cave" *ngIf="!started" (click)="start()"><i class="far fa-play-circle"></i> Rozpocznij test</button>
<button class="btn btn-study-cave" *ngIf="checked && not_last" (click)="increment()"><i class="fas fa-arrow-right"></i>Przejdź dalej</button>
<button class="btn btn-study-cave" *ngIf="checked && !not_last" (click)="increment()"><i class="far fa-stop-circle"></i> Zakończ test</button>
</div>
<br />
<button class="btn btn-study-cave" routerLink="/flashcards/sets/{{ id }}"><i class="fas fa-arrow-left"></i> Wróć do zestawu</button>
</div>
</div>
</div>

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsMemoryTestComponent } from './flashcards-memory-test.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { AuthenticationService } from '../../authentication.service';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsMemoryTestComponent', () => {
let component: FlashcardsMemoryTestComponent;
let fixture: ComponentFixture<FlashcardsMemoryTestComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsMemoryTestComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsMemoryTestComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,102 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { Subscription } from 'rxjs/Subscription';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-flashcards-memory-test',
templateUrl: './flashcards-memory-test.component.html',
styleUrls: ['./flashcards-memory-test.component.css']
})
export class FlashcardsMemoryTestComponent implements OnInit, OnDestroy {
id: number;
flashcardSubscribtionMeta: Subscription;
flashcardSubscribtion: Subscription;
name: String;
category: String;
length_test: number;
flashcards: number;
length_packages: number;
goodNow: number;
started: Boolean = false;
finish: Boolean = false;
checked: Boolean = false;
not_last: Boolean = true;
packages: Array<Object> = [];
filled = 0;
good = 0;
package_id = 0;
constructor(private flashcardsService: FlashcardsService, private route: ActivatedRoute) {}
ngOnInit() {
this.id = this.route.snapshot.params.id;
this.flashcardSubscribtionMeta = this.flashcardsService.getSet(this.id).subscribe(data => {
this.name = data['name'];
this.category = data['category'];
});
this.flashcardSubscribtion = this.flashcardsService.getTestMemory(this.id).subscribe(data => {
this.length_test = data.length;
this.flashcards = data.length / 2;
this.createPackages(data);
});
}
createPackages(data) {
let attribute = '0';
let set = [];
const n = this.length_test;
for (let i = 0; i < n; i++) {
if (i % 10 === 0) {
this.packages[attribute] = {
set: set,
};
attribute = (i / 10).toString();
set = [];
}
set.push(data[i]);
if (i === (n - 1)) {
this.packages[attribute] = {
set: set,
};
}
}
this.length_packages = this.packages.length;
if (this.packages.length < 2) {
this.not_last = false;
}
}
start() {
this.started = true;
}
increment() {
if (this.package_id === (this.length_packages - 2)) {
this.not_last = false;
}
if (this.package_id === (this.length_packages - 1)) {
this.started = false;
this.finish = true;
} else {
this.package_id += 1;
}
}
goodEvent(goods) {
this.goodNow = goods;
this.filled = this.goodNow;
this.good = this.goodNow;
}
isChecked(check) {
this.checked = check;
}
ngOnDestroy() {
this.flashcardSubscribtionMeta.unsubscribe();
this.flashcardSubscribtion.unsubscribe();
}
}

View File

@ -0,0 +1,94 @@
#container-small {
background-color: #181616;
padding: 30px;
width: 25%;
height: 400px;
margin: 25px;
float:left;
box-sizing: border-box;
position: relative;
text-align: center;
}
.content{
position:fixed;
width:100%;
box-sizing: border-box;
max-width: 100%;
}
#container-large {
background-color: #181616;
padding: 30px;
width: 65%;
height: 400px;
margin: 25px;
float: right;
box-sizing: border-box;
position: relative;
text-align: center;
}
.title{
font-size: 48px;
}
.text-content{
font-size: 18px;
}
.puzzle{
display: flex;
min-height: 50px;
min-width: 100px;
background-color: rgb(206, 168, 86);
color: black;
align-items: center;
justify-content: center;
margin: 5px 0 5px 5px;
cursor: pointer;
}
.puzzle-right{
display: flex;
color: black;
min-height: 50px;
min-width: 100px;
background-color: rgb(202, 144, 17);
align-items: center;
justify-content: center;
margin: 5px 5px 5px 0;
}
.both-sides{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.puzzle:hover{
border: 3px solid gray;
}
.active{
border: 3px solid white;
}
.puzzles-container{
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.empty{
background-color: gray;
cursor: not-allowed;
}
.good{
background-color: green;
}
.wrong{
background-color: red;
}

View File

@ -0,0 +1,31 @@
<div>
<h3>Dostępne lewe strony:</h3>
<div class="puzzles-container">
<div class='puzzle' *ngFor="let item of leftSides; let i = index" (click)="leftSideClick(item,i)" [ngClass]="{'active': (selectedLeftSide.leftSideText === item && selectedLeftSide.indexOfLeftSide === i && selectedLeftSide.from === 'leftSides'), 'empty': (item === undefined || item === '')}">
<span>{{item.left_side}}</span>
</div>
</div>
<h3>Twoja odpowiedź:</h3>
<div class="puzzles-container">
<div *ngFor="let item of leftSidesToSend; let i = index" class="both-sides">
<div class="puzzle good" *ngIf="result[i] && verify">
<span>{{item.left_side}}
</span>
</div>
<div class="puzzle wrong" *ngIf="!result[i] && verify">
<span>{{item.left_side}}
</span>
</div>
<div *ngIf="!verify" class='puzzle' (click)="leftSideToSendClick(item,i)" [ngClass]="{'active': (selectedLeftSide.leftSideText === item && selectedLeftSide.indexOfLeftSide === i && selectedLeftSide.from === 'leftSidesToSend')}">
<span>{{item.left_side}}
</span>
</div>
<div class='puzzle-right'>
<span>{{rightSides[i].right_side}}</span>
</div>
</div>
</div>
<br />
<button *ngIf="!verify" class="btn btn-study-cave" (click)="nextQuestion()"><i class="fas fa-check-circle"></i> Sprawdź</button>
</div>

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsPairsTestSetComponent } from './flashcards-pairs-test-set.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { AuthenticationService } from '../../authentication.service';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsPairsTestSetComponent', () => {
let component: FlashcardsPairsTestSetComponent;
let fixture: ComponentFixture<FlashcardsPairsTestSetComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsPairsTestSetComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsPairsTestSetComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,121 @@
import { Component, OnInit, OnChanges, SimpleChanges, Input, Output, EventEmitter, OnDestroy } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { Subscription } from 'rxjs/Subscription';
import * as $ from 'jquery';
import { ISubscription } from 'rxjs/Subscription';
@Component({
selector: 'app-flashcards-pairs-test-set',
templateUrl: './flashcards-pairs-test-set.component.html',
styleUrls: ['./flashcards-pairs-test-set.component.css']
})
export class FlashcardsPairsTestSetComponent implements OnInit, OnChanges, OnDestroy {
@Input() package: Array<any>;
@Input() id: number;
@Input() package_id: number;
@Output() goodEvent = new EventEmitter();
@Output() isChecked = new EventEmitter();
private verifyAnswerSubscription: ISubscription;
leftSides = [];
rightSides = [];
leftSidesToSend = [];
result = [];
verify = false;
good = 0;
selectedLeftSide = { indexOfLeftSide: Number, left_side: String, from: '', id: Number };
constructor(private flashcardsService: FlashcardsService) { }
ngOnInit() {
this.isChecked.emit(false);
this.prepareLists();
}
ngOnChanges(changes: SimpleChanges) {
this.prepareLists();
}
ngOnDestroy() {
if (this.verifyAnswerSubscription) {
this.verifyAnswerSubscription.unsubscribe();
}
}
nextQuestion(f) {
let canSend = true;
this.good = 0;
this.result = [];
this.leftSides.forEach(element => {
if (element !== '') {
canSend = false;
}
});
if (canSend) {
this.verify = true;
this.leftSidesToSend.forEach((element, index) => {
const body = {
content: this.rightSides[index].right_side,
id: element.id,
side: 'left'
};
this.verifyAnswerSubscription = this.flashcardsService.testCheck(this.id, body).subscribe(d => {
this.result[index] = d.result;
if (d.result) {
this.good += 1;
}
if (this.result.length === this.leftSidesToSend.length) {
this.send();
}
});
});
}
}
send() {
this.isChecked.emit(true);
this.goodEvent.emit(this.good);
}
leftSideClick(item, i) {
if (item !== '' && item !== undefined) {
if (this.selectedLeftSide.from === 'leftSides') {
this.selectedLeftSide = { indexOfLeftSide: undefined, left_side: undefined, from: undefined, id: undefined };
} else {
this.selectedLeftSide = { indexOfLeftSide: i, left_side: item.left_side, from: 'leftSides', id: item.id };
}
}
}
leftSideToSendClick(item, i) {
if (this.selectedLeftSide.from === 'leftSides') {
const helper = this.leftSidesToSend[i];
this.leftSidesToSend[i] = this.selectedLeftSide;
this.leftSides[Number(this.selectedLeftSide.indexOfLeftSide)] = helper;
this.selectedLeftSide = { indexOfLeftSide: undefined, left_side: undefined, from: undefined, id: undefined };
} else if (this.selectedLeftSide.from === 'leftSidesToSend') {
const helper = this.leftSidesToSend[i];
this.leftSidesToSend[i] = this.selectedLeftSide;
this.leftSidesToSend[Number(this.selectedLeftSide.indexOfLeftSide)] = helper;
this.selectedLeftSide = { indexOfLeftSide: undefined, left_side: undefined, from: undefined, id: undefined };
} else if (item !== '' && item !== undefined) {
this.selectedLeftSide = { indexOfLeftSide: i, left_side: item.left_side, from: 'leftSidesToSend', id: item.id };
}
}
prepareLists() {
this.selectedLeftSide = { indexOfLeftSide: undefined, left_side: undefined, from: undefined, id: undefined };
this.leftSides = [];
this.rightSides = [];
this.leftSidesToSend = [];
this.verify = false;
this.leftSides = JSON.parse(JSON.stringify(this.package[0].setLeft));
this.rightSides = JSON.parse(JSON.stringify(this.package[0].setRight));
this.leftSides.forEach(element => {
this.leftSidesToSend.push('');
});
}
}

View File

@ -0,0 +1,51 @@
#container-small {
background-color: #181616;
padding: 30px;
width: 32%;
height: 400px;
box-sizing: border-box;
position: relative;
text-align: center;
}
.content {
display: flex;
justify-content: space-around;
align-content: center;
margin-top: 2rem;
}
#container-large {
background-color: #181616;
padding: 30px;
width: 62%;
min-height: 400px;
text-align: center;
}
.container {
margin-bottom: 4rem;
}
@media screen and (max-width: 800px) {
.content {
flex-wrap: wrap;
}
#container-small {
height: 250px;
}
#container-small, #container-large {
width: 90%;
margin-bottom: 2.5rem;
}
}
@media screen and (max-width: 350px) {
#container-small {
height: 350px;
}
}

View File

@ -0,0 +1,55 @@
<div class="content">
<div id="container-small">
<h4>Łączenie fiszek w pary</h4>
<br />
<div>
<b>Nazwa zestawu: </b>
<i>{{ name }}</i>
<br />
<b>Kategoria: </b>
<i>{{ category }}</i>
</div>
<div *ngIf="started">
<div>
<div>
<b>Uzupełniono: </b>
<span> {{ filled }} z {{ length_test }} fiszek</span>
</div>
<div>
<b>Poprawnych odpowiedzi: </b>
<span> {{ good }} na {{ length_test }}</span>
</div>
<div>
<b>Złych odpowiedzi: </b>
<span> {{ bad }} na {{ length_test }}</span>
</div>
</div>
<br />
</div>
</div>
<div id="container-large">
<div class="test" *ngIf="started">
<app-flashcards-pairs-test-set (goodEvent)="goodEvent($event)" (isChecked)=isChecked($event)
[package]="packages" package_id="{{ package_id }}" [id]="id"></app-flashcards-pairs-test-set>
<br />
<div>
<b>Ekran: </b>
<span> {{ package_id + 1}} z {{ length_packages }}</span>
</div>
</div>
<div *ngIf="finish">
<app-test-results [result]="good" [maxPts]="length_test"></app-test-results>
</div>
<br />
<div *ngIf="!finish">
<button class="btn btn-study-cave" *ngIf="!started" (click)="start()"><i class="far fa-play-circle"></i> Rozpocznij test</button>
<button class="btn btn-study-cave" *ngIf="checked && not_last" (click)="increment()"><i class="far fa-play-circle"></i> Przejdź dalej</button>
<button class="btn btn-study-cave" *ngIf="checked && !not_last" (click)="increment()"><i class="far fa-stop-circle"></i> Zakończ test</button>
</div>
<br />
<button class="btn btn-study-cave" routerLink="/flashcards/sets/{{ id }}"><i class="fas fa-arrow-left"></i> Wróć do zestawu</button>
</div>
</div>

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsPairsTestComponent } from './flashcards-pairs-test.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { AuthenticationService } from '../../authentication.service';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsPairsTestComponent', () => {
let component: FlashcardsPairsTestComponent;
let fixture: ComponentFixture<FlashcardsPairsTestComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsPairsTestComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsPairsTestComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,110 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { Subscription } from 'rxjs/Subscription';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-flashcards-pairs-test',
templateUrl: './flashcards-pairs-test.component.html',
styleUrls: ['./flashcards-pairs-test.component.css']
})
export class FlashcardsPairsTestComponent implements OnInit, OnDestroy {
id: number;
flashcardSubscribtionMeta: Subscription;
flashcardSubscribtion: Subscription;
name: String;
category: String;
length_test: number;
length_packages: number;
goodNow: number;
started: Boolean = false;
finish: Boolean = false;
left: Boolean = true;
right: Boolean = false;
checked: Boolean = false;
not_last: Boolean = true;
packages: Array<Object> = [];
filled = 0;
good = 0;
bad = 0;
package_id = 0;
constructor(private flashcardsService: FlashcardsService, private route: ActivatedRoute) {}
ngOnInit() {
this.id = this.route.snapshot.params.id;
this.flashcardSubscribtionMeta = this.flashcardsService.getSet(this.id).subscribe(data => {
this.name = data['name'];
this.category = data['category'];
});
this.flashcardSubscribtion = this.flashcardsService.getTestPairing(this.id).subscribe(data => {
this.length_test = data['left'].length;
this.createPackages(data);
});
}
createPackages(data) {
let attribute = '0';
let setLeft = [];
let setRight = [];
const n = this.length_test;
for (let i = 0; i < n; i++) {
if (i % 5 === 0) {
this.packages[attribute] = {
setLeft: setLeft,
setRight: setRight
};
attribute = (i / 5).toString();
setLeft = [];
setRight = [];
}
setLeft.push(data['left'][i]);
setRight.push(data['right'][i]);
if (i === (n - 1)) {
this.packages[attribute] = {
setLeft: setLeft,
setRight: setRight
};
}
}
this.length_packages = this.packages.length;
if (this.packages.length < 2) {
this.not_last = false;
}
}
start() {
this.started = true;
}
increment() {
if (this.package_id === (this.length_packages - 2)) {
this.not_last = false;
}
if (this.package_id === (this.length_packages - 1)) {
this.started = false;
this.finish = true;
} else {
this.package_id += 1;
}
}
goodEvent(goods) {
this.goodNow = goods;
const filledNow = this.packages[this.package_id]['setLeft'].length;
this.filled = filledNow;
this.good = this.goodNow;
this.bad = this.filled - this.good;
}
isChecked(check) {
this.checked = check;
}
ngOnDestroy() {
this.flashcardSubscribtionMeta.unsubscribe();
this.flashcardSubscribtion.unsubscribe();
}
}

View File

@ -0,0 +1,65 @@
.listItem:hover {
background-color: rgb(206, 168, 86);
}
.wrapper{
width: 100%;
padding: 30px;
min-height: 100%;
}
.btn{
margin-bottom: 5px;
}
.content{
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
}
.buttons-container > div{
margin: 10px;
}
.puzzle{
min-height: 50px;
min-width: 100px;
background-color: rgb(206, 168, 86);
align-items: center;
color: black;
justify-content: center;
margin: 5px 5px 5px 5px;
}
.puzzle-right{
min-height: 50px;
min-width: 100px;
color: black;
background-color: rgb(202, 144, 17);;
align-items: center;
justify-content: center;
margin: 5px 5px 5px 5px;
}
.both-sides{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.puzzles-container{
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.empty{
background-color: gray;
cursor: not-allowed;
}

View File

@ -0,0 +1,45 @@
<div class="wrapper">
<div class="content">
<div class="button-container">
<button class="btn btn-study-cave" (click)="goBack()"><i class="fas fa-arrow-left"></i> POWRÓT</button>
<button class="btn btn-study-cave" (click)="showTestTypeMenu()"><i class="fas fa-pencil-ruler"></i> GENERUJ TESTY</button>
<button *ngIf="owned && set && set.permission !== 'GROUP'" class="btn btn-study-cave" (click)="navigateToEditMode()"><i class="fas fa-edit"></i> EDYTUJ FISZKI</button>
<button *ngIf="owned && set && set.permission !== 'GROUP'" class="btn btn-study-cave" (click)="showPopup()"><i class="fas fa-trash-alt"></i> USUŃ FISZKI</button>
<div *ngIf="user && owned && set && set.permission !== 'GROUP'">
<button class="btn btn-study-cave" (click)="changePermission()"><i class="fas fa-unlock"></i> ZMIEŃ UPRAWNIENIA</button>
</div>
</div>
<app-flashcards-test-type-menu [id]="id" (cancel)="handleCancelFlashcardsTestyTypeMenu($event)" *ngIf="testTypeMenu"></app-flashcards-test-type-menu>
<br />
<div *ngIf="set">
<h2>{{set.name}}</h2>
<table class="table">
<thead>
<th>
Lewa strona
</th>
<th>
Prawa strona
</th>
</thead>
<tbody>
<tr *ngFor="let flashcard of set.flashcards; let i = index">
<td class="puzzle">{{flashcard.left_side}}</td>
<td class="puzzle-right">{{flashcard.right_side}}</td>
</tr>
</tbody>
</table>
</div>
<app-comments [what]="'flashcardsets'"></app-comments>
</div>
<p-dialog header="Potwierdź" [(visible)]="display">
Czy chcesz usunąć fiszkę?
<p-footer>
<button type="button" pButton icon="pi pi-check" (click)="deleteSet()" label="TAK"></button>
<button type="button" pButton icon="pi pi-close" (click)="display=false" label="NIE" class="ui-button-secondary"></button>
</p-footer>
</p-dialog>
</div>

View File

@ -0,0 +1,65 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsSetDetailComponent } from './flashcards-set-detail.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { AuthenticationService } from '../../authentication.service';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { By } from '@angular/platform-browser';
import { RoutingStateService } from '../../routing-state.service';
describe('FlashcardsSetDetailComponent', () => {
let component: FlashcardsSetDetailComponent;
let fixture: ComponentFixture<FlashcardsSetDetailComponent>;
let mockSet: any;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsSetDetailComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService, RoutingStateService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsSetDetailComponent);
component = fixture.componentInstance;
mockSet = {
name: 'testName',
flashcards: [
{
left_side: 'testLeftSide',
right_side: 'testRightSide'
}
]
};
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
// testowanie DOM
it('should render set name', () => {
component.set = mockSet;
fixture.detectChanges();
const setName = document.querySelector('h2').innerHTML;
expect(setName).toEqual(mockSet.name);
});
it('should render flashcards sides', () => {
component.set = mockSet;
fixture.detectChanges();
const leftSide = document.querySelector('.puzzle').innerHTML;
expect(leftSide).toEqual('testLeftSide');
const rightSide = document.querySelector('.puzzle-right').innerHTML;
expect(rightSide).toEqual('testRightSide');
});
});

View File

@ -0,0 +1,102 @@
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { Set } from '../set';
import { ActivatedRoute, Router } from '@angular/router';
import { FlashcardsService } from '../flashcards.service';
import { Subscription } from 'rxjs/Subscription';
import { MatSnackBar } from '@angular/material/snack-bar';
import { RoutingStateService } from '../../routing-state.service';
@Component({
selector: 'app-flashcards-set-detail',
templateUrl: './flashcards-set-detail.component.html',
styleUrls: ['./flashcards-set-detail.component.css']
})
export class FlashcardsSetDetailComponent implements OnInit, OnDestroy {
id: number;
set: any;
flashcardSubscribtion: Subscription;
testTypeMenu = false;
user: Boolean = false;
ShowStatus: Boolean = false;
permission: string;
owner;
owned: Boolean = false;
display = false;
constructor(private route: ActivatedRoute, private flashcardsService: FlashcardsService, private router: Router,
private routingState: RoutingStateService, public snackBar: MatSnackBar) { }
ngOnInit() {
const previousUrl = this.routingState.getPreviousUrl();
if (previousUrl.indexOf('test-gen') === -1) {
sessionStorage.setItem('previousUrl', previousUrl);
}
this.id = this.route.snapshot.params.id;
this.flashcardSubscribtion = this.flashcardsService.getSet(this.id).subscribe(data => { this.set = data; });
this.IsLogin();
this.owner = this.flashcardsService.getOwner();
this.isOwner();
}
isOwner() {
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
if (localStorage.getItem('currentUser')) {
if (currentUser.username === this.owner) {
this.owned = true;
}
} else {
this.owned = false;
}
}
showTestTypeMenu() {
this.testTypeMenu = true;
}
IsLogin() {
if (localStorage.getItem('currentUser')) {
this.user = true;
} else {
this.user = false;
}
}
showPopup() {
this.display = true;
}
changePermission(): void {
if (this.set.permission === 'Public') {
this.permission = 'Private';
} else {
this.permission = 'Public';
}
this.flashcardsService.changeSetPermission(this.id, this.permission);
this.snackBar.open('Zmieniono pozwolenie na: ' + this.permission, null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-success'] });
}
handleCancelFlashcardsTestyTypeMenu(e) {
this.testTypeMenu = e;
}
navigateToEditMode() {
this.router.navigate(['flashcards/sets/edit', this.id]);
}
ngOnDestroy() {
this.flashcardSubscribtion.unsubscribe();
}
deleteSet() {
const data = this.id;
this.flashcardSubscribtion = this.flashcardsService.deleteSet(data);
this.display = false;
}
goBack() {
const previousUrl = sessionStorage.getItem('previousUrl');
sessionStorage.removeItem('previousUrl');
this.router.navigate([previousUrl]);
}
}

View File

@ -0,0 +1,46 @@
.listItem:hover {
background-color: rgb(206, 168, 86);
}
.listItem:active{
background-color: #4BC7FA;
}
.listItem{
cursor: pointer;
}
.wrapper{
width: 100%;
padding: 30px;
min-height: 100%;
margin-bottom: 40px;
}
@media screen and (max-width: 800px) {
.mobile{
display: none;
}
}
.empty{
text-align: center;
}
.content{
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
margin-left: auto;
margin-right: auto;
}
ag-grid-angular{
margin: 10px;
}
.buttons-container > div{
margin: 10px;
}

View File

@ -0,0 +1,35 @@
<div class="wrapper">
<div class="content">
<app-flashcards></app-flashcards>
<br />
<h1>FISZKI</h1>
<br />
<div class="buttons-container">
<div *ngIf="logged">
<button class="btn btn-study-cave" (click)="toFlashcardsMaker()"><i class="fas fa-plus"></i> Dodaj fiszki</button>
</div>
<div *ngIf="logged" class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-study-cave active" (click)="ShowPublic()">
<input type="radio" name="options" id="option1" autocomplete="off" checked><i class="fas fa-globe"></i> Publiczne
</label>
<label class="btn btn-study-cave" (click)="ShowPrivate()">
<input type="radio" name="options" id="option2" autocomplete="off"><i class="fas fa-user-secret"></i> Prywatne
</label>
</div>
</div>
<br />
<ag-grid-angular style="width: 100%; height: 475px;" class="ag-theme-dark" [rowData]="sets" [columnDefs]="columnDefs" [enableSorting]="true"
[enableFilter]="true" (gridReady)="onGridReady($event)" (gridSizeChanged)="onGridSizeChanged($event)" [gridOptions]="gridOptions"
[pagination]="true" [paginationAutoPageSize]="true" (rowClicked)="onRowClicked($event)" [localeText]="localeText"
(gridColumnsChanged)="onGidColumnsChanged($event)">
</ag-grid-angular>
<p-dialog header="Potwierdź" [(visible)]="display">
Czy chcesz usunąć fiszkę?
<p-footer>
<button type="button" pButton icon="pi pi-check" (click)="deleteSet()" label="TAK"></button>
<button type="button" pButton icon="pi pi-close" (click)="display=false" label="NIE" class="ui-button-secondary"></button>
</p-footer>
</p-dialog>
</div>
</div>

View File

@ -0,0 +1,35 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsSetsListComponent } from './flashcards-sets-list.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { AuthenticationService } from '../../authentication.service';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsSetsListComponent', () => {
let component: FlashcardsSetsListComponent;
let fixture: ComponentFixture<FlashcardsSetsListComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsSetsListComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsSetsListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,271 @@
import { Component, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { Set } from '../set';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { FilterPipe } from '../../filter.pipe';
import { GridOptions, RowDoubleClickedEvent } from 'ag-grid-community/main';
import localeText from './localeText';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-flashcards-sets-list',
templateUrl: './flashcards-sets-list.component.html',
styleUrls: ['./flashcards-sets-list.component.css']
})
export class FlashcardsSetsListComponent implements OnInit, OnDestroy {
sets = [];
privatesets = [];
setsEmpty = true;
selectedSet: Set;
flashcardSubscription: Subscription;
flashcardSubscriptionOwners: Subscription;
user: Boolean;
ShowStatus: Boolean = false;
owner;
searchPublic = 'Public';
searchOwner;
gridApi;
gridColumnApi;
public gridOptions: GridOptions;
localeText = localeText;
logged = false;
publicMode = true;
permission;
display = false;
isGroup = false;
columnDefs = [
{ headerName: 'Nazwa', field: 'name', headerTooltip: 'Nazwa' },
{ headerName: 'Data dodania', field: 'add_date', headerTooltip: 'Data dodania', hide: false },
{ headerName: 'Data modyfikacji', field: 'edit_date', headerTooltip: 'Data modyfikacji', hide: false },
{ headerName: 'Właściciel', field: 'owner', headerTooltip: 'Właściciel', hide: false },
{ headerName: 'Grupa', field: 'group', headerTooltip: 'Grupa', hide: !this.isGroup },
{
headerName: '',
suppressMenu: true,
suppressSorting: true,
cellRenderer: this.customCellRendererFunc,
hide: !this.isGroup
}
];
setToDelete: any;
constructor(private flashcardsService: FlashcardsService, private router: Router, public snackBar: MatSnackBar) { }
customCellRendererFunc(params) {
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
if (!currentUser) {
return '';
} else if (params.data['owner'] === currentUser.username) {
return `
<button type="button" data-action-type="remove" class="btn btn-study-cave btn-sm" title="Usuń">
<i class="fas fa-trash-alt" data-action-type="remove"></i></button>
<button type="button" data-action-type="changePermission" class="btn btn-study-cave btn-sm" title="Uprawnienia">
<i class="fas fa-unlock" data-action-type="changePermission"></i></button>
`;
} else {
return '';
}
}
onSelect(set: Set): void {
this.selectedSet = set;
this.router.navigate(['flashcards/sets', this.selectedSet.id]);
this.flashcardsService.setOwner(this.selectedSet.owner);
}
toFlashcardsMaker() {
this.router.navigate(['flashcards/add']);
}
public onRowClicked(e) {
if (e.event.target !== undefined) {
const data = e.data;
const actionType = e.event.target.getAttribute('data-action-type');
switch (actionType) {
case 'remove':
return this.onActionRemoveClick(e);
case 'changePermission':
return this.changePermission(e);
default:
return this.goToSets(e);
}
}
}
public onActionRemoveClick(e) {
this.display = true;
this.setToDelete = e.data.id;
}
deleteSet() {
const data = this.setToDelete;
this.flashcardSubscription = this.flashcardsService.deleteSet(data);
this.display = false;
}
changePermission(e): void {
if (e.data.permission === 'Public') {
this.permission = 'Private';
} else {
this.permission = 'Public';
}
this.flashcardsService.changeSetPermission(e.data.id, this.permission);
this.snackBar.open('Zmieniono pozwolenie na: ' + this.permission, null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-success'] });
}
goToSets(e) {
this.router.navigate(['flashcards/sets', e.data.id]);
this.flashcardsService.setOwner(e.data.owner);
}
IsLogin() {
const own = JSON.parse(localStorage.getItem('currentUser'));
if (localStorage.getItem('currentUser')) {
this.user = true;
this.searchOwner = own.username;
} else {
this.user = false;
this.searchOwner = ' ';
}
}
ShowPublic() {
this.sets = [];
this.ShowStatus = false;
this.getSets();
}
ShowPrivate() {
this.privatesets = [];
this.ShowStatus = true;
this.getSetsOwners();
}
ngOnInit() {
if (localStorage.getItem('currentUser')) {
this.logged = true;
}
this.gridOptions = {
rowHeight: 50,
headerHeight: 25,
getRowStyle: function (params) {
return {
cursor: 'pointer'
};
},
};
this.getSets();
this.IsLogin();
this.refreshColumns();
}
getSets(): void {
this.isGroup = false;
this.flashcardSubscription = this.flashcardsService.getSets()
.subscribe(data => {
this.sets = data;
if (this.sets.length > 0) {
this.setsEmpty = false;
} else {
this.setsEmpty = true;
}
this.refreshColumns();
});
}
getSetsOwners(): void {
this.isGroup = true;
this.flashcardSubscriptionOwners = this.flashcardsService.getSetsOwners()
.subscribe(data => {
data.forEach((x, i) => {
if (!x['group']) {
x['group'] = 'Brak';
}
});
this.sets = data;
if (this.sets.length > 0) {
this.setsEmpty = false;
} else {
this.setsEmpty = true;
}
this.refreshColumns();
});
}
onGridReady(params) {
this.gridApi = params.api;
this.gridColumnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
onGridSizeChanged(params) {
if (params.clientWidth < 800) {
this.columnDefs = [
{ headerName: 'Nazwa', field: 'name', headerTooltip: 'Nazwa' },
{ headerName: 'Data dodania', field: 'add_date', headerTooltip: 'Data dodania', hide: true },
{ headerName: 'Data modyfikacji', field: 'edit_date', headerTooltip: 'Data modyfikacji', hide: true },
{ headerName: 'Właściciel', field: 'owner', headerTooltip: 'Właściciel', hide: true },
{ headerName: 'Grupa', field: 'group', headerTooltip: 'Grupa', hide: !this.isGroup },
{
headerName: '',
suppressMenu: true,
suppressSorting: true,
cellRenderer: this.customCellRendererFunc,
hide: !this.isGroup
}
];
} else {
this.columnDefs = [
{ headerName: 'Nazwa', field: 'name', headerTooltip: 'Nazwa' },
{ headerName: 'Data dodania', field: 'add_date', headerTooltip: 'Data dodania', hide: false },
{ headerName: 'Data modyfikacji', field: 'edit_date', headerTooltip: 'Data modyfikacji', hide: false },
{ headerName: 'Właściciel', field: 'owner', headerTooltip: 'Właściciel', hide: false },
{ headerName: 'Grupa', field: 'group', headerTooltip: 'Grupa', hide: !this.isGroup },
{
headerName: '',
suppressMenu: true,
suppressSorting: true,
cellRenderer: this.customCellRendererFunc,
hide: !this.isGroup
}
];
}
params.api.sizeColumnsToFit();
}
onGidColumnsChanged(params) {
params.api.sizeColumnsToFit();
}
ngOnDestroy() {
if (this.flashcardSubscription) {
this.flashcardSubscription.unsubscribe();
}
if (this.flashcardSubscriptionOwners) {
this.flashcardSubscriptionOwners.unsubscribe();
}
}
refreshColumns() {
this.columnDefs = [
{ headerName: 'Nazwa', field: 'name', headerTooltip: 'Nazwa' },
{ headerName: 'Data dodania', field: 'add_date', headerTooltip: 'Data dodania', hide: false },
{ headerName: 'Data modyfikacji', field: 'edit_date', headerTooltip: 'Data modyfikacji', hide: false },
{ headerName: 'Właściciel', field: 'owner', headerTooltip: 'Właściciel', hide: false },
{ headerName: 'Grupa', field: 'group', headerTooltip: 'Grupa', hide: !this.isGroup },
{
headerName: '',
suppressMenu: true,
suppressSorting: true,
cellRenderer: this.customCellRendererFunc,
hide: !this.isGroup
}
];
}
}

View File

@ -0,0 +1,90 @@
const localeText = {
// for filter panel
page: 'Strona',
more: 'Więcej',
to: 'do',
of: 'z',
next: '>',
last: '>>',
first: '<<',
previous: '<',
loadingOoo: 'Ładowanie...',
// for set filter
selectAll: 'Wybierz wszystkie',
searchOoo: 'Szukaj',
blanks: 'Luki',
// for number filter and text filter
filterOoo: 'Filtruj...',
applyFilter: 'Zastosuj filtr',
// for number filter
equals: 'Równy',
notEqual: 'Nie równy',
lessThan: 'Mniejszy niż',
greaterThan: 'Większy niż',
// for text filter
contains: 'Zawiera',
notContains: 'Nie zawiera',
startsWith: 'Zaczyna się od',
endsWith: 'Kończy się na',
// the header of the default group column
group: 'Grupa',
// tool panel
columns: 'Kolumny',
rowGroupColumns: 'Grupa wiersza kolumn',
rowGroupColumnsEmptyMessage: 'Przeciągnij kolumny do grupy',
valueColumns: 'Wartości kolumn',
pivotMode: 'Pivot-Mode',
groups: 'Grupy',
values: 'Wartości',
pivots: 'Pivots',
valueColumnsEmptyMessage: 'Przeciągnij kolumny do agregacji',
pivotColumnsEmptyMessage: 'Przeciągnij kolumny na oś',
toolPanelButton: 'Narzędzia',
// other
noRowsToShow: 'Brak fiszek do wyświetlenia',
// enterprise menu
pinColumn: 'Przypiąta kolumna',
valueAggregation: 'Sumuj',
autosizeThiscolumn: 'Autosize this column',
autosizeAllColumns: 'Autosize All Columns',
groupBy: 'Grupuj po',
ungroupBy: 'Nie grupuj po',
resetColumns: 'Resetuj kolumny',
expandAll: 'Rozszerz wszystko',
collapseAll: 'Zwiń wszystko',
toolPanel: 'Narzędzia',
export: 'Export',
csvExport: 'CSV Export',
excelExport: 'Excel Export',
// enterprise menu pinning
pinLeft: 'Przypnij <<',
pinRight: 'Przypnij >>',
noPin: 'Nie przypinaj <>',
// enterprise menu aggregation and status panel
sum: 'Suma',
min: 'Minimum',
max: 'Maksimum',
none: 'Nic',
count: 'Ilość',
average: 'Średnia',
// standard menu
copy: 'Kopiuj',
copyWithHeaders: 'Kopiuj z nagłówkami',
ctrlC: 'ctrl + C',
paste: 'wklej',
ctrlV: 'ctrl + C'
};
export default localeText;

View File

@ -0,0 +1,4 @@
div{
margin-top: 5px;
margin-bottom: 5px;
}

View File

@ -0,0 +1,7 @@
<div>
<button class="btn btn-study-cave" (click)="goToTestGenFill()">Uzupełnij</button>
<button class="btn btn-study-cave" (click)="goToTestGen()">Pary</button>
<button class="btn btn-study-cave" (click)="goToTestGenTyperace()">Na czas</button>
<button class="btn btn-study-cave" (click)="goToTestGenMemory()">Memory</button>
<button class="btn btn-study-cave" (click)="cancelMenu()"><i class="fas fa-times"></i> Anuluj</button>
</div>

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsTestTypeMenuComponent } from './flashcards-test-type-menu.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { AuthenticationService } from '../../authentication.service';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsTestTypeMenuComponent', () => {
let component: FlashcardsTestTypeMenuComponent;
let fixture: ComponentFixture<FlashcardsTestTypeMenuComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsTestTypeMenuComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsTestTypeMenuComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,37 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-flashcards-test-type-menu',
templateUrl: './flashcards-test-type-menu.component.html',
styleUrls: ['./flashcards-test-type-menu.component.css']
})
export class FlashcardsTestTypeMenuComponent implements OnInit {
@Input() id: Number;
@Output() cancel = new EventEmitter;
cancelMenu() {
this.cancel.emit(false);
}
goToTestGen() {
this.router.navigate(['flashcards/test-gen/flashcards-pairs', this.id]);
}
goToTestGenFill() {
this.router.navigate(['flashcards/test-gen/flashcards-filling-in', this.id]);
}
goToTestGenMemory() {
this.router.navigate(['flashcards/test-gen/flashcards-memory', this.id]);
}
goToTestGenTyperace() {
this.router.navigate(['flashcards/test-gen/flashcards-typerace', this.id]);
}
constructor(private router: Router) { }
ngOnInit() {
}
}

View File

@ -0,0 +1,43 @@
#container-small {
background-color: #181616;
padding: 30px;
width: 32%;
min-height: 400px;
box-sizing: border-box;
position: relative;
text-align: center;
}
.content {
display: flex;
justify-content: space-around;
align-content: center;
margin-top: 2rem;
}
#container-large {
background-color: #181616;
padding: 30px;
width: 62%;
min-height: 400px;
text-align: center;
}
.container {
margin-bottom: 4rem;
}
@media screen and (max-width: 800px) {
.content {
flex-wrap: wrap;
}
#container-small {
height: 250px;
}
#container-small, #container-large {
width: 90%;
margin-bottom: 2.5rem;
}
}

View File

@ -0,0 +1,62 @@
<div class="content">
<div id="container-small">
<h4>Przepisywanie fiszek na czas</h4>
<br />
<div>
<b>Nazwa zestawu: </b>
<i>{{ name }}</i>
<br />
<b>Kategoria: </b>
<i>{{ category }}</i>
</div>
<div *ngIf="started">
<div>
<div>
<b>Uzupełniono: </b>
<span> {{ filled }} z {{ length_test }} fiszek</span>
</div>
<div>
<b>Poprawnych odpowiedzi: </b>
<span> {{ good }} na {{ length_test }}</span><br />
<b>Złych odpowiedzi: </b>
<span> {{ bad }} na {{ length_test }}</span><br />
<div *ngIf="this.filled < this.length_test && !finish">
<b>Twój czas: </b>
<span> {{minute}} m {{second}} s {{millisecond}} ms</span>
</div>
</div>
</div>
</div>
</div>
<div id="container-large">
<div *ngIf="started">
<div *ngIf="this.filled >= 1 && !finish">
Poprzednia odpowiedź:<br />
{{flashcards[index-1].content}} {{stoptimes[index-1].m}} m {{stoptimes[index-1].s}} s {{stoptimes[index-1].ms}} ms
</div>
<div *ngIf="this.filled < this.length_test && !finish">
Podaj odpowiedź:<br />
{{flashcards[index].content}}
<input appAutofocus [(ngModel)]="answer" class="mr-2" placeholder="odpowiedź" (keyup.enter)="verifyAnswer()" >
<button class="btn btn-study-cave" (click)="verifyAnswer()" ><i class="fas fa-check-circle"></i> Sprawdź</button>
</div>
<div *ngIf="this.is_correct === true && !finish">ODPOWIEDŹ POPRAWNA</div>
<div *ngIf="this.is_correct === false && !finish">ODPOWIEDŹ NIEPOPRAWNA</div>
<div *ngIf="this.filled < this.length_test-1 && !finish">
Następne pytanie:<br />
{{flashcards[index+1].content}}
</div>
</div>
<div *ngIf="finish">
<app-test-results [result]="good" [maxPts]="length_test" [minutes]="finalminute" [seconds]="finalsecond" [milliseconds]="finalmillisecond" [timer]="true"></app-test-results>
</div>
<div *ngIf="!finish">
<button class="btn btn-study-cave" *ngIf="!started" (click)="start()"><i class="far fa-play-circle"></i> Rozpocznij test</button>
</div>
<button class="btn btn-study-cave" *ngIf="finish" routerLink="/flashcards/sets/{{ id }}"><i class="far fa-stop-circle"></i> Zakończ test</button>
<button class="btn btn-study-cave" *ngIf="started && !finish && !not_last"(click)="finished()"><i class="far fa-stop-circle"></i> Przejdź do wyniku</button>
<br />
<button class="btn btn-study-cave" routerLink="/flashcards/sets/{{ id }}"><i class="fas fa-arrow-left"></i> Wróć do zestawu</button>
</div>
</div>

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsTyperaceTestComponent } from './flashcards-typerace-test.component';
import { RouterTestingModule } from '@angular/router/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { AuthenticationService } from '../../authentication.service';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('FlashcardsTyperaceTestComponent', () => {
let component: FlashcardsTyperaceTestComponent;
let fixture: ComponentFixture<FlashcardsTyperaceTestComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsTyperaceTestComponent ],
schemas: [NO_ERRORS_SCHEMA],
providers: [FlashcardsService, AuthenticationService],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsTyperaceTestComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,167 @@
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
import { FlashcardsService } from '../flashcards.service';
import { Subscription } from 'rxjs/Subscription';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-flashcards-typerace-test',
templateUrl: './flashcards-typerace-test.component.html',
styleUrls: ['./flashcards-typerace-test.component.css']
})
export class FlashcardsTyperaceTestComponent implements OnInit, OnDestroy {
id: number;
private flashcardSubscribtionMeta: Subscription;
private flashcardSubscribtion: Subscription;
private flashcardSubscribtionCheck: Subscription;
name: String;
category: String;
length_test: number;
goodNow: number;
started: Boolean = false;
finish: Boolean = false;
flashcards: Array<Object> = [];
filled = 0;
good = 0;
bad = 0;
answer: String;
index = 0;
not_last: Boolean = true;
is_correct: Boolean;
allAnswers = [];
stoptimes = [];
hour= 0;
minute= 0;
second= 0;
millisecond= 0;
starttime= false;
pause= false;
x= 10;
intervalId;
finalhour= 0;
finalminute= 0;
finalsecond= 0;
finalmillisecond= 0;
constructor(private flashcardsService: FlashcardsService, private route: ActivatedRoute) { }
ngOnInit() {
this.id = this.route.snapshot.params.id;
this.flashcardSubscribtionMeta = this.flashcardsService.getSet(this.id).subscribe(data => {
this.name = data['name'];
this.category = data['category'];
});
this.flashcardSubscribtion = this.flashcardsService.getTestFilling(this.id).subscribe(data => {
this.length_test = data.length;
this.flashcards = data;
});
}
start() {
this.started = true;
this.onStart();
}
finished() {
this.finish = true;
this.onPause();
}
verifyAnswer() {
this.reset();
const body = [];
const n = this.length_test;
if (this.answer === '') {
this.answer = ' ';
}
this.allAnswers.push({
id: this.index,
answer: this.answer,
});
body.push({
id: this.flashcards[this.index]['id'],
content: this.answer,
side: this.flashcards[this.index]['side'],
});
this.flashcardSubscribtionCheck = this.flashcardsService.testCheck(this.id, body[0])
.subscribe(data => {
this.is_correct = data.result;
if (this.is_correct === true) {
this.good = this.good + 1;
} else {
this.bad = this.bad + 1;
}
});
if (this.index < this.length_test) {
this.index = this.index + 1;
this.filled = this.filled + 1;
this.answer = '';
}
if (this.index === this.length_test) {
this.not_last = false;
}
}
reset() {
this.finalhour = this.finalhour + this.hour;
this.finalminute = this.finalminute + this.minute;
this.finalsecond = this.finalsecond + this.second;
this.finalmillisecond = this.finalmillisecond + this.millisecond;
this.stoptimes.push({
id: this.index,
m: this.minute,
s: this.second,
ms: this.millisecond,
});
this.x = 0;
this.hour = this.minute = this.second = this.millisecond = 0;
this.starttime = false;
this.pause = false;
clearInterval(this.intervalId);
this.onStart();
}
onStart() {
this.x = 10;
this.starttime = true;
this.intervalId = setInterval(() => {
this.updateTime();
}, 100);
}
onPause() {
this.pause = true;
clearInterval(this.intervalId);
}
updateTime() {
this.millisecond += this.x;
if (this.millisecond > 90) {
this.millisecond = 0;
this.second++;
}
if (this.second > 59) {
this.second = 0;
this.minute++;
}
if (this.minute > 59) {
this.minute = 0;
this.hour++;
}
}
ngOnDestroy() {
this.flashcardSubscribtionMeta.unsubscribe();
this.flashcardSubscribtion.unsubscribe();
if (this.flashcardSubscribtionCheck) {
this.flashcardSubscribtionCheck.unsubscribe();
}
}
}

View File

@ -0,0 +1,66 @@
import * as $ from 'jquery';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { HttpClientModule } from '@angular/common/http';
import { FlashcardsComponent } from './flashcards/flashcards.component';
import { FlashcardsSetsListComponent } from './flashcards-sets-list/flashcards-sets-list.component';
import { FlashcardsService } from './flashcards.service';
import { FlashcardsAddComponent } from './flashcards-add/flashcards-add.component';
import { FlashcardsAddTableComponent } from './flashcards-add-table/flashcards-add-table.component';
import { FlashcardsAddCsvComponent } from './flashcards-add-csv/flashcards-add-csv.component';
import { FlashcardsSetDetailComponent } from './flashcards-set-detail/flashcards-set-detail.component';
import { FlashcardsEditTableComponent } from './flashcards-edit-table/flashcards-edit-table.component';
import { FlashcardsPairsTestComponent } from './flashcards-pairs-test/flashcards-pairs-test.component';
import { FlashcardsPairsTestSetComponent } from './flashcards-pairs-test-set/flashcards-pairs-test-set.component';
import { FlashcardsTestTypeMenuComponent } from './flashcards-test-type-menu/flashcards-test-type-menu.component';
import { FlashcardsFillingInTestComponent } from './flashcards-filling-in-test/flashcards-filling-in-test.component';
import { FlashcardsMemoryTestComponent } from './flashcards-memory-test/flashcards-memory-test.component';
import { FlashcardsMemoryTestSetComponent } from './flashcards-memory-test-set/flashcards-memory-test-set.component';
import { FlashcardsTyperaceTestComponent } from './flashcards-typerace-test/flashcards-typerace-test.component';
import { RouterModule } from '@angular/router';
import { TestResultsComponent } from './test-results/test-results.component';
import { FilterUserPipe } from '../filter-user.pipe';
import { FilterPipe } from '../filter.pipe';
import { AgGridModule } from 'ag-grid-angular';
import { DialogModule } from 'primeng/dialog';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { SharedModule } from '../shared/shared.module';
@NgModule({
imports: [
RouterModule,
BrowserModule,
FormsModule,
HttpModule,
HttpClientModule,
AgGridModule.withComponents([]),
DialogModule,
ConfirmDialogModule,
SharedModule
],
declarations: [
TestResultsComponent,
FlashcardsComponent,
FlashcardsSetsListComponent,
FlashcardsAddComponent,
FlashcardsAddTableComponent,
FlashcardsAddCsvComponent,
FlashcardsSetDetailComponent,
FlashcardsEditTableComponent,
FlashcardsPairsTestComponent,
FlashcardsPairsTestSetComponent,
FlashcardsTestTypeMenuComponent,
FlashcardsFillingInTestComponent,
FlashcardsMemoryTestComponent,
FlashcardsMemoryTestSetComponent,
FlashcardsTyperaceTestComponent,
FilterUserPipe,
FilterPipe
],
providers: [FlashcardsService, FilterPipe, FilterUserPipe],
})
export class FlashcardsModule { }

View File

@ -0,0 +1,23 @@
import { TestBed, inject } from '@angular/core/testing';
import { FlashcardsService } from './flashcards.service';
import { RouterTestingModule } from '@angular/router/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { AuthenticationService } from '../authentication.service';
describe('FlashcardsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [FlashcardsService, AuthenticationService],
schemas: [NO_ERRORS_SCHEMA],
imports: [ RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule]
});
});
it('should be created', inject([FlashcardsService], (service: FlashcardsService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,160 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpRequest, HttpEvent } from '@angular/common/http';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { Set } from './set';
import 'rxjs/add/operator/map';
import { Router } from '@angular/router';
import { AuthenticationService } from '../authentication.service';
import { THIS_EXPR } from '@angular/compiler/src/output/output_ast';
import { MatSnackBar } from '@angular/material/snack-bar';
@Injectable()
export class FlashcardsService {
// tslint:disable-next-line:max-line-length
constructor(private httpClient: HttpClient, private router: Router, private authenticationService: AuthenticationService,
public snackBar: MatSnackBar) { this.setHeaders(); }
private headers;
owner;
setHeaders() {
if (localStorage.getItem('currentUser')) {
this.headers = new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': '' + this.authenticationService.getToken()
});
} else {
this.headers = new HttpHeaders({
'Content-Type': 'application/json'
});
}
}
changeSetPermission(id, permission) {
this.setHeaders();
this.httpClient.put('sets/' + id + '/permission', permission, { headers: this.headers })
.subscribe();
}
add(body) {
const url = 'sets/';
this.sendData(url, body);
}
edit(body) {
const url = 'sets/';
this.putData(url, body);
}
getSets(): Observable<any> {
this.setHeaders();
return this.httpClient.get('sets', { headers: this.headers, params: { permission: 'Public' } });
}
getSetsOwners(): Observable<any> {
this.setHeaders();
const owner = JSON.parse(localStorage.getItem('currentUser'));
return this.httpClient.get('sets', { headers: this.headers, params: { owner: owner.username } });
}
setOwner(owner) {
if (owner === null) {
this.owner = ' ';
} else {
this.owner = owner;
}
}
getOwner() {
return this.owner;
}
sendData(url, body) {
this.setHeaders();
this.httpClient.post(url, body, { headers: this.headers, observe: 'response' })
.subscribe(data => { this.sendResponse(data); },
error => {
this.snackBar.open('Coś poszło nie tak. Spróbuj ponownie później.', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
}
);
}
putData(url, body) {
this.setHeaders();
this.httpClient.put(url, body, { headers: this.headers, observe: 'response' })
.subscribe(data => { this.sendResponse(data); },
error => {
this.snackBar.open('Coś poszło nie tak. Spróbuj ponownie później.', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
}
);
}
sendResponse(data) {
if (data.status === 200) {
this.snackBar.open('Operacja przebiegła pomyślnie!', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-success'] });
this.router.navigate(['flashcards/sets']);
} else {
this.snackBar.open('Coś poszło nie tak. Spróbuj ponownie później.', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
}
}
// sending file - import flashcards from CSV
pushFileToStorage(file: File, user: string, permission: string, url: string): Observable<HttpEvent<{}>> {
const formdata: FormData = new FormData();
formdata.append('file', file);
formdata.append('owner', user);
formdata.append('permission', permission);
const req = new HttpRequest('POST', url, formdata, {
reportProgress: true,
responseType: 'text'
});
return this.httpClient.request(req);
}
getSet(id) {
return this.httpClient.get('sets/' + id + '/');
}
getTestPairing(id) {
return this.httpClient.get('sets/' + id + '/test/pairing/');
}
getTestFilling(id) {
return this.httpClient.get('sets/' + id + '/test/filling-in/').map((data: Array<Object>) => data);
}
getTestMemory(id) {
return this.httpClient.get('sets/' + id + '/test/memory/').map((data: Array<Object>) => data);
}
deleteSet(id) {
this.setHeaders();
return this.httpClient.delete('sets/' + id, { headers: this.headers, observe: 'response' })
.subscribe(data => { this.sendResponse(data); },
error => {
this.snackBar.open('Coś poszło nie tak. Spróbuj ponownie później.', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
}
);
}
// sprawdzanie testu - zapytanie dla jednej fiszki
testCheck(id, body) {
// id - id zestawu fiszek
// ciało body = {id - idFiszki, content - wpisana odpowiedź, side - strona fiszki, którą widział użytkownik}
this.setHeaders();
return this.httpClient.get(`sets/${id}/${body['id']}/${body['content']}/${body['side']}/test/check/`, { headers: this.headers })
.map((data: any) => data);
}
testMemory(id, body) {
this.setHeaders();
return this.httpClient.get(`sets/${id}/test/memory/check?x=${body['x']}&y=${body['y']}`, { headers: this.headers })
.map((data: any) => data);
}
}

View File

@ -0,0 +1,3 @@
.btn{
margin-bottom: 5px;
}

View File

@ -0,0 +1,4 @@
<div>
<button class="btn btn-study-cave" routerLink="/flashcards/sets"><i class="fas fa-scroll"></i> FISZKI</button>
<button class="btn btn-study-cave" routerLink="/materials/list"><i class="fas fa-book-open"></i> MATERIAŁY</button>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FlashcardsComponent } from './flashcards.component';
describe('FlashcardsComponent', () => {
let component: FlashcardsComponent;
let fixture: ComponentFixture<FlashcardsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FlashcardsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FlashcardsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-flashcards',
templateUrl: './flashcards.component.html',
styleUrls: ['./flashcards.component.css']
})
export class FlashcardsComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

10
src/app/flashcards/set.ts Normal file
View File

@ -0,0 +1,10 @@
export interface Set {
id: number;
name: string;
category: string;
owner: string;
add_date: string;
edit_date: string;
grade: number;
permission: string;
}

View File

@ -0,0 +1,6 @@
<h2>Twój wynik to {{(result/maxPts*100) | number : '1.0-2' }}%!</h2>
<h3>Poprawne odpowiedzi: {{result}}</h3>
<h3>Niepoprawne odpowiedzi: {{maxPts-result}}</h3>
<div *ngIf="timer">
<h3>Twój czas: {{minutes}} m {{seconds}} s {{milliseconds}} ms</h3>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TestResultsComponent } from './test-results.component';
describe('TestResultsComponent', () => {
let component: TestResultsComponent;
let fixture: ComponentFixture<TestResultsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TestResultsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestResultsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,20 @@
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-test-results',
templateUrl: './test-results.component.html',
styleUrls: ['./test-results.component.css']
})
export class TestResultsComponent implements OnInit {
@Input() result: number;
@Input() maxPts: number;
@Input() minutes: number;
@Input() seconds: number;
@Input() milliseconds: number;
@Input() timer: Boolean;
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,10 @@
.footer{
color: white;
text-align: center;
position: fixed;
bottom: 0;
width: 100%;
max-width: 100%;
background-color: rgb(24, 22, 22);
padding: 1rem 0;
}

View File

@ -0,0 +1,3 @@
<div class="footer">
Regulamin | Polityka Cookies | © 2018 StudyCave
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterComponent } from './footer.component';
describe('FooterComponent', () => {
let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FooterComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.css']
})
export class FooterComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,40 @@
label {
display: block;
}
.container {
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
}
.wrapper-add {
width: 100%;
display: flex;
justify-content: center;
align-content: center;
margin-bottom: 4rem;
}
.wrapper {
width: 100%;
padding: 30px;
min-height: 100%;
margin-bottom: 40px;
}
.content {
background-color: #181616;
padding: 30px;
margin-top: 2rem;
margin-bottom: 2rem;
text-align: center;
margin-left: auto;
margin-right: auto;
}
.buttons-container > div {
margin: 10px;
}

View File

@ -0,0 +1,35 @@
<div class="wrapper">
<div class="wrapper-add">
<div class="container">
<h1>Tworzenie grupy</h1>
<br />
<form ngNativeValidate action="/" method="post" #user="ngForm" (ngSubmit)="add(user.value)">
<label>
Wprowadź nazwę dla grupy:
<input type="text" name="name" maxlength="150" class="form-control" ngModel required />
</label>
<br />
<label>
Wprowadź opis grupy:
<textarea name="description" class="form-control" ngModel required></textarea>
</label>
<br /><br />
<button #btn class="btn btn-study-cave" type="submit"><i class="fas fa-check-circle"></i> Dodaj grupę!</button>
<button class="btn btn-study-cave" routerLink="/my-groups"><i class="fas fa-arrow-left"></i> Powrót</button>
</form>
</div>
</div>
</div>
<p-dialog header="Informacja" [(visible)]="showInfoDialog" [responsive]="true"
[minWidth]="300" [minY]="70" [draggable]="false" [closable]="false">
<p>Twoja grupa została utworzona prawidłowo.</p>
<p>Poniżej znajduje się kod dostępu poprzez który możesz dodawać członków:</p>
<!--<p><b>{{ createdGroup.key }}</b></p>-->
<p><b>{{ createdGroup.groupKey }}</b></p>
<br />
<p>Kod ten będzie dostępny także w sekcji "Zarządzaj grupą."</p>
<br />
<!-- To check later -->
<button class="btn btn-study-cave" (click)="showInfoDialog = false" [routerLink]="['/groups/manage', createdGroup.id]">Rozumiem</button>
</p-dialog>

View File

@ -0,0 +1,49 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { GroupCreatorComponent } from './group-creator.component';
import { GroupsService } from '../groups.service';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AuthenticationService } from '../../authentication.service';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
describe('GroupCreatorComponent', () => {
let component: GroupCreatorComponent;
let fixture: ComponentFixture<GroupCreatorComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [GroupCreatorComponent],
schemas: [NO_ERRORS_SCHEMA],
providers: [GroupsService, AuthenticationService],
imports: [RouterTestingModule, FormsModule, HttpClientModule, MatSnackBarModule, NoopAnimationsModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(GroupCreatorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should create group', async(() => {
fixture.whenStable().then(() => {
fixture.autoDetectChanges();
spyOn(component, 'add').and.callThrough();
fixture.debugElement.nativeElement.querySelectorAll('.form-control')[0].value = 'group-name';
fixture.debugElement.nativeElement.querySelectorAll('.form-control')[1].value = 'group-descripion';
fixture.debugElement.nativeElement.querySelectorAll('.btn-study-cave')[0].click();
expect(component.add).toHaveBeenCalled();
});
}));
});

View File

@ -0,0 +1,63 @@
import { Component, OnInit, OnDestroy, ElementRef, ViewChild } from '@angular/core';
import { ISubscription } from 'rxjs/Subscription';
import { Router } from '@angular/router';
import { MatSnackBar } from '@angular/material/snack-bar';
import { GroupsService } from '../groups.service';
import { Group } from '../group';
@Component({
selector: 'app-group-creator',
templateUrl: './group-creator.component.html',
styleUrls: ['./group-creator.component.css']
})
export class GroupCreatorComponent implements OnInit, OnDestroy {
public currentUser: string;
public postGroupsSubscription: ISubscription;
public createdGroup: Group = {};
public showInfoDialog: boolean;
@ViewChild('btn') btn: ElementRef;
constructor(private groupsService: GroupsService, private router: Router,
private snackBar: MatSnackBar) { }
ngOnInit() {
this.showInfoDialog = false;
if (localStorage.getItem('currentUser') !== null) {
this.currentUser = JSON.parse(localStorage.getItem('currentUser')).username;
}
}
add(value: any) {
this.btn.nativeElement.disabled = true;
const toSend = {
name: value.name,
description: value.description,
owner: this.currentUser
};
this.postGroupsSubscription = this.groupsService.postGroup(toSend).subscribe(
success => {
this.createdGroup = success;
this.showInfoDialog = true;
},
error => {
this.btn.nativeElement.disabled = false;
this.snackBar.open('Wystąpił błąd serwera. Spróbuj ponownie później.', null,
{ duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-error'] });
}
);
}
goToManageGroup() {
this.router.navigate(['groups/manage', this.createdGroup.id]);
}
ngOnDestroy() {
if (this.postGroupsSubscription) {
this.postGroupsSubscription.unsubscribe();
}
}
}

Some files were not shown because too many files have changed in this diff Show More