Skip to content

Commit

Permalink
feat: add pastes management page
Browse files Browse the repository at this point in the history
  • Loading branch information
neumanf committed Sep 19, 2024
1 parent f7cf225 commit 147946e
Show file tree
Hide file tree
Showing 11 changed files with 353 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
import com.mally.api.auth.AuthenticationManager;
import com.mally.api.auth.UserJwt;
import com.mally.api.pastebin.dtos.CreatePasteDTO;
import com.mally.api.pastebin.dtos.SearchPastesDTO;
import com.mally.api.pastebin.entities.Paste;
import com.mally.api.pastebin.services.PastebinService;
import com.mally.api.shared.rest.dtos.ApiResponseDTO;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

Expand All @@ -23,11 +24,22 @@ public class PastebinController {

private final PastebinService pastebinService;

@GetMapping("/search")
public ResponseEntity<Page<Paste>> search(@RequestParam String query, Pageable pageable) {
var userId = AuthenticationManager.getAuthenticatedUser().map(UserJwt::getId).orElse(null);
@GetMapping("/")
public ResponseEntity<Page<Paste>> findAll(
@RequestParam(required = false) String search,
@RequestParam(defaultValue = "0") int pageNumber,
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(defaultValue = "createdAt") String sortBy,
@RequestParam(defaultValue = "DESC") String orderBy
) {
var userId = AuthenticationManager.getAuthenticatedUser().map(UserJwt::getId).orElseThrow();
var result = pastebinService.findAll(
userId,
search,
PageRequest.of(pageNumber, pageSize, Sort.by(orderBy.equalsIgnoreCase("ASC") ? Sort.Direction.ASC : Sort.Direction.DESC, sortBy))
);

return ResponseEntity.ok().body(pastebinService.search(query, userId, pageable));
return ResponseEntity.ok(result);
}

@GetMapping("/paste/{slug}")
Expand All @@ -50,4 +62,18 @@ public ResponseEntity<ApiResponseDTO> paste(@Valid @RequestBody CreatePasteDTO d
.ok()
.body(ApiResponseDTO.success("Paste created successfully.", paste));
}

@DeleteMapping("/{id}")
public ResponseEntity<ApiResponseDTO> delete(@PathVariable Long id) {
pastebinService.delete(id);

return ResponseEntity.ok(ApiResponseDTO.success("Paste deleted successfully.", null));
}

@DeleteMapping("bulk")
public ResponseEntity<ApiResponseDTO> bulkDelete(@RequestParam List<Long> id) {
pastebinService.deleteMany(id);

return ResponseEntity.ok(ApiResponseDTO.success("Pastes deleted successfully.", null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,25 @@ public void deleteExpiredPastes() {
pastebinRepository.deleteExpiredPastes(ZonedDateTime.now());
}

public Page<Paste> search(String searchQuery, String userId, Pageable pageable) {
List<String> searchFields = List.of("slug", "text", "syntax", "createdAt", "expiresAt");
public Long getStats(String userId) {
return pastebinRepository.countByUserId(userId);
}

public Page<Paste> findAll(String userId, String search, Pageable pageable) {
if (search != null && !search.isEmpty()) {
List<String> searchFields = List.of("slug", "text", "syntax");

return PaginationUtils.paginateSearch(entityManager, Paste.class, searchFields, search, userId, pageable);
}

return PaginationUtils.paginateSearch(entityManager, Paste.class, searchFields, searchQuery, userId, pageable);
return pastebinRepository.findAllByUserId(userId, pageable);
}

public Long getStats(String userId) {
return pastebinRepository.countByUserId(userId);
public void delete(Long id) {
pastebinRepository.deleteById(id);
}

public void deleteMany(List<Long> ids) {
pastebinRepository.deleteAllById(ids);
}
}
5 changes: 5 additions & 0 deletions apps/ui/src/app/dashboard/dashboard-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core';
import { DashboardIndexComponent } from './pages/index/dashboard-index.component';
import { RouterModule, Routes } from '@angular/router';
import { UrlsComponent } from './pages/urls/urls.component';
import { PastesComponent } from './pages/pastes/pastes.component';

const routes: Routes = [
{
Expand All @@ -12,6 +13,10 @@ const routes: Routes = [
path: 'short-urls',
component: UrlsComponent,
},
{
path: 'pastes',
component: PastesComponent,
},
];

@NgModule({
Expand Down
8 changes: 7 additions & 1 deletion apps/ui/src/app/dashboard/dashboard.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ import { ConfirmationService } from 'primeng/api';
import { StatsService } from '../stats/services/stats.service';
import { SkeletonModule } from 'primeng/skeleton';
import { StatsCardComponent } from './pages/index/stats-card/stats-card.component';
import { PastesComponent } from './pages/pastes/pastes.component';

@NgModule({
declarations: [DashboardIndexComponent, UrlsComponent, StatsCardComponent],
declarations: [
DashboardIndexComponent,
UrlsComponent,
StatsCardComponent,
PastesComponent,
],
imports: [
CommonModule,
DashboardRoutingModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

<div class="grid grid-cols-4 gap-4">
<ng-container *ngIf="!loading; else cardLoading">
<app-stats-card title="Short URLs created" [data]="stats?.urlShortener?.total" link="/dashboard/short-urls"></app-stats-card>
<app-stats-card title="Pastes created" [data]="stats?.pastebin?.total" link="/dashboard/pastes"></app-stats-card>
<app-stats-card title="Total Short URLs" [data]="stats?.urlShortener?.total" link="/dashboard/short-urls"></app-stats-card>
<app-stats-card title="Total Pastes" [data]="stats?.pastebin?.total" link="/dashboard/pastes"></app-stats-card>
</ng-container>
</div>

Expand Down
94 changes: 94 additions & 0 deletions apps/ui/src/app/dashboard/pages/pastes/pastes.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<p-toast />
<p-confirmDialog />

<p class="text-xl font-bold mb-5">Pastes</p>

<p-table
#dt
[value]="data?.content ?? []"
[totalRecords]="data?.totalElements ?? 0"
[rows]="data?.numberOfElements"
[lazy]="true"
(onLazyLoad)="onLoad($event)"
[loading]="loadingData"
[paginator]="true"
[sortOrder]="-1"
[tableStyle]="{ 'min-width': '75rem' }"
[(selection)]="selectedUrls"
[rowHover]="true"
dataKey="id"
currentPageReportTemplate="Showing {rows} of {totalRecords} entries"
[showCurrentPageReport]="true"
>
<ng-template pTemplate="caption">
<div class="flex items-center justify-between">
<span class="p-input-icon-left">
<i class="ti ti-search"></i>
<input
pInputText
type="text"
placeholder="Search..."
(change)="search($event)"
/>
</span>
<div>
<p-button severity="danger" label="Delete" icon="ti ti-trash" (onClick)="deleteMany()" [disabled]="selectedUrls.length == 0" />
</div>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th style="width: 4rem">
<p-tableHeaderCheckbox />
</th>
<th style="min-width: 8rem" pSortableColumn="slug">
URL <p-sortIcon field="slug" />
</th>
<th style="min-width: 8rem" pSortableColumn="syntax">
Syntax <p-sortIcon field="syntax" />
</th>
<th style="width: 8rem" pSortableColumn="text">
Text <p-sortIcon field="text" />
</th>
<th style="min-width: 12rem" pSortableColumn="createdAt">
Created at <p-sortIcon field="createdAt" />
</th>
<th style="min-width: 10rem" pSortableColumn="expiresAt">
Expires in <p-sortIcon field="expiresAt" />
</th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-paste>
<tr>
<td>
<p-tableCheckbox [value]="paste" />
</td>
<td>
<a
class="text-red-400"
[href]="window.location.origin + '/p/' + paste.slug"
>{{ window.location.hostname + '/p/' + paste.slug }}</a
>
</td>
<td>
<span class="bg-red-900 text-red-200 font-semibold p-1 rounded">
{{ paste.syntax }}
</span>
</td>
<td class="truncate max-w-96">
<code>{{ paste.text }}</code>
</td>
<td>
{{ paste.createdAt | date: 'short' }}
</td>
<td>
<span [pTooltip]="pasteExpiresIn(paste.expiresAt, 'long')" tooltipPosition="top">{{ pasteExpiresIn(paste.expiresAt, 'humanized') }}</span>
</td>
<td>
<p-menu #options [model]="optionsItems" [popup]="true" appendTo="body" />
<p-button (onClick)="openOptionsMenu(options, $event, paste)" icon="ti ti-dots-vertical"/>
</td>
</tr>
</ng-template>
</p-table>
Empty file.
Loading

0 comments on commit 147946e

Please sign in to comment.