Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
650775a
style: change font and polish UI
markstockhausen Jan 27, 2026
79e45a2
style: update font weight
markstockhausen Jan 27, 2026
430e1c2
style: conform to Prettier formatting
markstockhausen Feb 3, 2026
73a402b
Implement optimized Scheduling Service and Pipeline Logic
markstockhausen Feb 3, 2026
8850cf6
style: update comments
markstockhausen Feb 3, 2026
fe676c3
Implement AI suggestions
markstockhausen Feb 9, 2026
b199432
Update Axios, Optimise scheduling pipeline
markstockhausen Feb 9, 2026
67d6e58
Optimise pipeline logic and clean up code
markstockhausen Feb 10, 2026
60d7f66
Optimise pipeline
markstockhausen Feb 10, 2026
6251a3a
Update logic for bidirectional relationship types
markstockhausen Feb 10, 2026
e683410
Streamline implementation
markstockhausen Feb 10, 2026
141bebd
Conform to Prettier formatting
markstockhausen Feb 10, 2026
64c4f20
Implement AI suggestions
markstockhausen Feb 17, 2026
d863e77
Further AI suggestions
markstockhausen Feb 17, 2026
846fd3d
Merge branch 'main' into feature/implement-scheduling-service
markstockhausen Feb 17, 2026
7916901
feat: add dashboard with contribution heatmap, badges, and session su…
markstockhausen Feb 17, 2026
4b03244
style: fix Prettier formatting
markstockhausen Feb 17, 2026
2190551
Update heatmap
markstockhausen Feb 24, 2026
785d9fa
Conform to Prettier formatting
markstockhausen Feb 24, 2026
2704820
Fix security audit
markstockhausen Feb 24, 2026
32ccd39
feat: Add initial version of onboarding
markstockhausen Feb 25, 2026
99f89e1
fix onboarding flow
markstockhausen Feb 25, 2026
4a8eeba
conform to Prettier formatting
markstockhausen Feb 25, 2026
49e98c2
Update profile setup
markstockhausen Feb 27, 2026
513e8c4
Fix skip feature, update package-lock.json
markstockhausen Mar 2, 2026
47042eb
Add milestone celebration toasts during mapping sessions
markstockhausen Mar 3, 2026
23c8d8b
Update screenshot on main page, extract CompetencyNetworkViz into reu…
markstockhausen Mar 3, 2026
2d85326
Merge remote-tracking branch 'origin/main' into feature/onboarding
markstockhausen Mar 4, 2026
6f48404
Fix package-lock.json
markstockhausen Mar 4, 2026
333175a
Merge branch 'main' into feature/onboarding
markstockhausen Mar 4, 2026
305892f
Conform to Prettier formatting
markstockhausen Mar 4, 2026
ed96f32
Merge branch 'feature/onboarding' of https://github.com/ls1intum/memo…
markstockhausen Mar 4, 2026
44e8467
Fix package-lock.json
markstockhausen Mar 4, 2026
0cb944d
Fix package-lock.json
markstockhausen Mar 4, 2026
f05a802
Implement AI suggestions
markstockhausen Mar 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,092 changes: 3,544 additions & 548 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@
"dependencies": {
"@radix-ui/react-label": "2.1.8",
"@radix-ui/react-radio-group": "1.3.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "1.2.4",
"@tanstack/react-query": "^5.90.16",
"axios": "^1.13.2",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"cmdk": "^1.1.1",
"keycloak-js": "^26.2.2",
"lucide-react": "0.525.0",
"motion": "12.23.24",
"radix-ui": "^1.4.3",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-router-dom": "^7.1.1",
"sonner": "^2.0.7",
"tailwind-merge": "3.3.1",
"tailwind-variants": "3.1.1"
},
Expand Down
Binary file added public/sessionPreview2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
package de.tum.cit.memo.controller;

import de.tum.cit.memo.dto.CreateCompetencyRelationshipRequest;
import de.tum.cit.memo.entity.CompetencyRelationship;
import de.tum.cit.memo.service.CompetencyRelationshipService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* Controller for competency relationship CRUD.
* For creating relationships and voting, use {@link SchedulingController}.
*/
@RestController
@RequestMapping("/api/competency-relationships")
@RequiredArgsConstructor
Expand All @@ -27,13 +26,6 @@ public class CompetencyRelationshipController {

private final CompetencyRelationshipService relationshipService;

@PostMapping
@Operation(summary = "Create a new competency relationship")
public ResponseEntity<CompetencyRelationship> createRelationship(@Valid @RequestBody CreateCompetencyRelationshipRequest request) {
CompetencyRelationship relationship = relationshipService.createRelationship(request);
return ResponseEntity.status(HttpStatus.CREATED).body(relationship);
}

@GetMapping("/{id}")
@Operation(summary = "Get competency relationship by ID")
public ResponseEntity<CompetencyRelationship> getRelationshipById(@PathVariable String id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.tum.cit.memo.controller;

import de.tum.cit.memo.dto.ContributorStatsResponse;
import de.tum.cit.memo.service.ContributorStatsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
@Tag(name = "Contributor Stats", description = "Endpoints for contributor statistics and gamification")
public class ContributorStatsController {

private final ContributorStatsService contributorStatsService;

@GetMapping("/{userId}/stats")
@Operation(summary = "Get contributor statistics", description = "Returns contribution stats including total votes, streaks, daily counts for heatmap, "
+ "and earned badge IDs.")
public ResponseEntity<ContributorStatsResponse> getContributorStats(@PathVariable String userId) {
ContributorStatsResponse stats = contributorStatsService.getStats(userId);
return ResponseEntity.ok(stats);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package de.tum.cit.memo.controller;

import de.tum.cit.memo.dto.RelationshipTaskResponse;
import de.tum.cit.memo.dto.VoteRequest;
import de.tum.cit.memo.dto.VoteResponse;
import de.tum.cit.memo.service.SchedulingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/scheduling")
@RequiredArgsConstructor
@Tag(name = "Scheduling", description = "Endpoints for competency mapping scheduling")
public class SchedulingController {

private final SchedulingService schedulingService;

@GetMapping("/next-relationship")
@Operation(summary = "Get next relationship to vote on", description = "Returns a competency pair for the user to map. Uses coverage (70%) and consensus (30%) pipelines. Returns 204 if no tasks remain.")
public ResponseEntity<RelationshipTaskResponse> getNextRelationship(
@RequestHeader("X-User-Id") String userId,
@org.springframework.web.bind.annotation.RequestParam(required = false) java.util.List<String> skippedIds) {
return schedulingService.getNextTask(userId, skippedIds)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.noContent().build());
}

@PostMapping("/vote")
@Operation(summary = "Submit a vote on a relationship", description = "Records the user's vote on a competency relationship. Accepts either a relationshipId or originId+destinationId pair (for swapped direction). MATCHES and UNRELATED votes are bidirectional.")
public ResponseEntity<VoteResponse> submitVote(
@RequestHeader("X-User-Id") String userId,
@Valid @RequestBody VoteRequest request) {
VoteResponse response = schedulingService.submitVote(userId, request);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.tum.cit.memo.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContributorStatsResponse {

private int totalVotes;
private int currentStreak;
private int longestStreak;
private List<DailyCount> dailyCounts;
private List<String> earnedBadges;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class DailyCount {
private LocalDate date;
private int count;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.tum.cit.memo.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RelationshipTaskResponse {

private String relationshipId;
private CompetencyInfo origin;
private CompetencyInfo destination;
private String pipeline;
private VoteCounts currentVotes;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class CompetencyInfo {
private String id;
private String title;
private String description;
}
}
18 changes: 18 additions & 0 deletions server/src/main/java/de/tum/cit/memo/dto/VoteCounts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.tum.cit.memo.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VoteCounts {

private int assumes;
private int extendsRelation;
private int matches;
private int unrelated;
}
36 changes: 36 additions & 0 deletions server/src/main/java/de/tum/cit/memo/dto/VoteRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package de.tum.cit.memo.dto;

import de.tum.cit.memo.enums.RelationshipType;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VoteRequest {

/**
* Relationship ID (used directly when direction is not swapped).
* Optional if originId + destinationId are provided instead.
*/
private String relationshipId;

/**
* Origin competency ID (used when direction is swapped).
* Must be provided together with destinationId.
*/
private String originId;

/**
* Destination competency ID (used when direction is swapped).
* Must be provided together with originId.
*/
private String destinationId;

@NotNull(message = "Relationship type is required")
private RelationshipType relationshipType;
Comment thread
markstockhausen marked this conversation as resolved.
Outdated
}
17 changes: 17 additions & 0 deletions server/src/main/java/de/tum/cit/memo/dto/VoteResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package de.tum.cit.memo.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VoteResponse {

private boolean success;
private VoteCounts updatedVotes;
private double newEntropy;
}
3 changes: 3 additions & 0 deletions server/src/main/java/de/tum/cit/memo/entity/Competency.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@ public class Competency {
@CreationTimestamp
@Column(nullable = false, updatable = false)
private Instant createdAt;

@Column(nullable = false)
private int degree;
}
Loading