Skip to content

Commit

Permalink
Merge branch 'develop' into whitesource/configure
Browse files Browse the repository at this point in the history
  • Loading branch information
FelixTJDietrich authored Aug 13, 2024
2 parents 6cd47e3 + 8283e3b commit 738ab64
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 28 deletions.
2 changes: 1 addition & 1 deletion webapp/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const preview: Preview = {
light: '',
dark: 'dark bg-background',
},
defaultTheme: 'dark',
defaultTheme: 'light',
}),
applicationConfig(appConfig),
],
Expand Down
10 changes: 10 additions & 0 deletions webapp/src/app/@types/github.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export namespace GitHub {
export interface Contributor {
id: number;
login: string;
url: string;
html_url: string;
avatar_url: string;
// More fields can be added here
}
}
53 changes: 53 additions & 0 deletions webapp/src/app/about/about.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<div class="flex flex-col gap-4">
<h1 class="text-3xl font-bold">About</h1>
<p class="mb-4">
Hephaestus leverages generative AI to streamline software development and enhance developer training. Focused on improving every phase of the software development lifecycle
(SDLC) and supporting agile practices, Hephaestus helps teams work more efficiently and adhere to best practices.
</p>
<h2 class="text-2xl font-semibold">Team</h2>
@if (query.isPending()) {
<span class="text-muted-foreground">Loading...</span>
} @else if (query.error()) {
<span class="text-destructive">An error has occurred</span>
}

@if (projectManager(); as projectManager) {
<div class="flex items-center gap-3 mb-4">
<a
[href]="projectManager.html_url"
target="_blank"
rel="noopener noreferrer"
class="hover:scale-105 transition-all hover:shadow-secondary-foreground/15 hover:shadow-lg rounded-full"
>
<app-avatar class="size-32">
<app-avatar-image [src]="projectManager.avatar_url" [alt]="projectManager.login + '\'s avatar'" />
<app-avatar-fallback>{{ projectManager.login.slice(0, 1).toUpperCase() }}</app-avatar-fallback>
</app-avatar>
</a>
<div>
<div class="text-2xl font-semibold">Felix T.J. Dietrich</div>
<div class="text-lg text-muted-foreground">Project Manager</div>
<a href="https://ase.cit.tum.de/people/dietrich/" target="_blank" rel="noopener noreferrer" class="text-primary underline-offset-4 hover:underline">Website</a>
</div>
</div>
}

@if (contributors(); as contributors) {
<h3 class="text-lg font-bold">Contributors</h3>
<div class="flex flex-wrap gap-2">
@for (contributor of contributors; track contributor.id) {
<a
[href]="contributor.html_url"
target="_blank"
rel="noopener noreferrer"
class="hover:scale-105 transition-all hover:shadow-secondary-foreground/15 hover:shadow-lg rounded-full"
>
<app-avatar class="size-20">
<app-avatar-image [src]="contributor.avatar_url" [alt]="contributor.login + '\'s avatar'" />
<app-avatar-fallback>{{ contributor.login.slice(0, 1).toUpperCase() }}</app-avatar-fallback>
</app-avatar>
</a>
}
</div>
}
</div>
42 changes: 42 additions & 0 deletions webapp/src/app/about/about.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { HttpClient } from '@angular/common/http';
import { Component, computed, inject } from '@angular/core';
import { injectQuery } from '@tanstack/angular-query-experimental';
import { lastValueFrom } from 'rxjs';
import { AvatarComponent } from 'app/ui/avatar/avatar.component';
import { AvatarImageComponent } from 'app/ui/avatar/avatar-image.component';
import { AvatarFallbackComponent } from 'app/ui/avatar/avatar-fallback.component';
import { ButtonComponent } from 'app/ui/button/button.component';
import { GitHub } from 'app/@types/github';

@Component({
selector: 'app-about',
standalone: true,
imports: [AvatarComponent, AvatarImageComponent, AvatarFallbackComponent, ButtonComponent],
templateUrl: './about.component.html'
})
export class AboutComponent {
http = inject(HttpClient);

query = injectQuery(() => ({
queryKey: ['contributors'],
queryFn: async () => lastValueFrom(this.http.get('https://api.github.com/repos/ls1intum/hephaestus/contributors')) as Promise<GitHub.Contributor[]>,
gcTime: Infinity
}));

projectManager = computed(() => {
const data = this.query.data();
if (!data) {
return undefined;
}
// 5898705 is the id of the project manager Felix T.J. Dietrich
return data.find((contributor) => contributor.id === 5898705);
});

contributors = computed(() => {
const data = this.query.data();
if (!data) {
return undefined;
}
return data.filter((contributor) => contributor.id !== 5898705);
});
}
18 changes: 18 additions & 0 deletions webapp/src/app/about/about.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { type Meta, type StoryObj } from '@storybook/angular';
import { AboutComponent } from './about.component';

const meta: Meta<AboutComponent> = {
title: 'Pages/About',
component: AboutComponent,
tags: ['autodocs']
};

export default meta;
type Story = StoryObj<AboutComponent>;

export const Default: Story = {
render: (args) => ({
props: args,
template: `<app-about />`
})
};
31 changes: 21 additions & 10 deletions webapp/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
<main class="container">
<header class="flex items-center justify-between py-4">
<a class="flex gap-2 items-center hover:text-muted-foreground" href="/">
<div class="flex flex-col min-h-dvh">
<header class="container flex items-center justify-between pt-4">
<a class="flex gap-2 items-center hover:text-muted-foreground" routerLink="/">
<lucide-angular name="hammer" class="size-6" />
<span class="text-xl font-semibold">Hephaestus</span>
</a>
<app-theme-switcher />
</header>
<div class="flex gap-2 m-2 flex-col items-start">
<app-counter title="First Counter" />
<app-counter title="Second Counter" [byCount]="16" />
</div>
<app-hello />
<router-outlet />
</main>
<main class="container flex-grow pt-4 pb-8">
<router-outlet />
</main>
<footer class="py-6 md:px-8 md:py-0 border-t">
<div class="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
<p class="text-balance text-center text-sm leading-loose text-muted-foreground md:text-left">
<a routerLink="about" class="font-medium underline underline-offset-4">About</a>
</p>
<p class="text-balance text-center text-sm leading-loose text-muted-foreground md:text-left">
Built by
<a href="https://github.com/ls1intum" target="_blank" rel="noreferrer" class="font-medium underline underline-offset-4">LS1 Team</a>
at
<a href="https://www.tum.de/en/" target="_blank" rel="noreferrer" class="font-medium underline underline-offset-4">TUM</a>. The source code is available on
<a href="https://github.com/ls1intum/hephaestus" target="_blank" rel="noreferrer" class="font-medium underline underline-offset-4"> GitHub</a>.
</p>
</div>
</footer>
</div>
6 changes: 2 additions & 4 deletions webapp/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { CounterComponent } from './example/counter/counter.component';
import { HelloComponent } from './example/hello/hello.component';
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
import { ThemeSwitcherComponent } from './components/theme-switcher/theme-switcher.component';
import { LucideAngularModule } from 'lucide-angular';

@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, CounterComponent, HelloComponent, LucideAngularModule, ThemeSwitcherComponent],
imports: [RouterOutlet, LucideAngularModule, ThemeSwitcherComponent, RouterLink, RouterLinkActive],
templateUrl: './app.component.html',
styles: []
})
Expand Down
7 changes: 6 additions & 1 deletion webapp/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { Routes } from '@angular/router';
import { AboutComponent } from 'app/about/about.component';
import { MainComponent } from 'app/main/main.component';

export const routes: Routes = [];
export const routes: Routes = [
{ path: '', component: MainComponent },
{ path: 'about', component: AboutComponent }
];
5 changes: 5 additions & 0 deletions webapp/src/app/main/main.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="flex gap-2 m-2 flex-col items-start">
<app-counter title="First Counter" />
<app-counter title="Second Counter" [byCount]="16" />
</div>
<app-hello />
11 changes: 11 additions & 0 deletions webapp/src/app/main/main.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
import { CounterComponent } from 'app/example/counter/counter.component';
import { HelloComponent } from 'app/example/hello/hello.component';

@Component({
selector: 'app-main',
standalone: true,
imports: [CounterComponent, HelloComponent],
templateUrl: './main.component.html'
})
export class MainComponent {}
12 changes: 7 additions & 5 deletions webapp/src/app/ui/avatar/avatar-fallback.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { cn } from 'app/utils';
import { ClassValue } from 'clsx';
import { injectAvatarConfig } from './avatar-config';
import { injectAvatar } from './avatar.component';
import { Subject, takeUntil, timer } from 'rxjs';

@Component({
selector: 'app-avatar-fallback',
Expand All @@ -17,20 +18,21 @@ export class AvatarFallbackComponent implements OnInit, OnDestroy {
private readonly avatar = injectAvatar();
private config = injectAvatarConfig();
private delayElapsed = signal(false);
private timeout: NodeJS.Timeout | null = null;
private destroy$ = new Subject<void>();

class = input<ClassValue>();
delayMs = input(this.config.delayMs);
computedClass = computed(() => cn('absolute inset-0 flex items-center justify-center rounded-full bg-muted', this.class()));
visible = computed(() => this.avatar.state() !== 'loaded' && this.delayElapsed());

ngOnInit(): void {
this.timeout = setTimeout(() => this.delayElapsed.set(true), this.delayMs());
timer(this.delayMs())
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.delayElapsed.set(true));
}

ngOnDestroy(): void {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.destroy$.next();
this.destroy$.complete();
}
}
11 changes: 4 additions & 7 deletions webapp/src/app/ui/avatar/avatar.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,13 @@ export default meta;
type Story = StoryObj<AvatarComponent>;

export const Default: Story = {
render: (args) => {
console.log(args);
return {
props: args,
template: `
render: (args) => ({
props: args,
template: `
<app-avatar>
<app-avatar-image ${argsToTemplate(args)}/>
<app-avatar-fallback ${argsToTemplate(args)}>CN</app-avatar-fallback>
</app-avatar>
`
};
}
})
};

0 comments on commit 738ab64

Please sign in to comment.