diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81487e3c93f238c18a141aa9fa5020795a89893f..29940aa9ac2d2967010188797e8c681b1ca42634 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,9 +12,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ## [UNRELEASED]
 
 ### Added
-
 ### Changed
-
 ### Fixed
 
+## [0.0.2]
+
+### Added
+
+- Allow users to see a list of featured puzzles from other users on their profile. (@kradziwilowicz)
 
+### Changed
+
+- Extend the Puzzle overviews to show creator and number of attempts (@cbaraya)
+- Add links to /attempts page to user profiles for admins (@MrHug)
+- Make link to /attempts page for admins (@MrHug)
+
+### Fixed
diff --git a/build.gradle.kts b/build.gradle.kts
index 295f71d98a2e4564320b9993b25b4b241521dd43..67797ff801ec127ead6ab5b74b811ba76b89c9dd 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,20 +2,20 @@ import com.diffplug.gradle.spotless.SpotlessExtension
 import org.springframework.boot.gradle.tasks.bundling.BootJar
 import nl.javadude.gradle.plugins.license.DownloadLicensesExtension
 import nl.javadude.gradle.plugins.license.LicenseExtension
+import java.nio.file.Files
 
 group = "nl.tudelft.pupple"
-version = "0.0.1"
+version = "0.0.2"
 
 val javaVersion = JavaVersion.VERSION_17
 
-val labradoorVersion = "1.4.1-SNAPSHOT"
-val libradorVersion = "1.0.3-SNAPSHOT7"
-val chihuahUIVersion = "1.0.2"
+val labradoorVersion = "1.4.1"
+val libradorVersion = "1.3.0"
+val chihuahUIVersion = "1.1.0"
+val guavaVersion = "32.1.3-jre"
 
 val genSourceDir = file("$buildDir/pupple/src/main/java")
 
-ext["log4j2.version"] = "2.16.0"
-
 repositories {
     mavenLocal()
     mavenCentral()
@@ -46,11 +46,11 @@ plugins {
     jacoco
     `maven-publish`
 
-    id("org.springframework.boot").version("2.5.6")
-    id("io.spring.dependency-management").version("1.0.11.RELEASE")
-    id("com.github.ben-manes.versions").version("0.39.0")
+    id("org.springframework.boot").version("2.7.18")
+    id("io.spring.dependency-management").version("1.1.4")
+    id("com.github.ben-manes.versions").version("0.50.0")
 
-    id("com.diffplug.spotless").version("6.0.1")
+    id("com.diffplug.spotless").version("6.23.3")
 
     id("com.github.hierynomus.license").version("0.16.1")
 }
@@ -140,17 +140,43 @@ val jacocoTestReport by tasks.getting(JacocoReport::class) {
         xml.required.set(true)
         csv.required.set(true)
 
-        //html.destination = file("$buildDir/reports/coverage")
+        html.outputLocation.set(file("$buildDir/reports/coverage"))
+    }
+}
+
+tasks.register("ensureDirectory") {
+    // Store target directory into a variable to avoid project reference in the configuration cache
+    val directory = file("src/main/resources/static/css")
+
+    doLast {
+        Files.createDirectories(directory.toPath())
     }
 }
 
+
 task<Exec>("sassCompile") {
-    if (System.getProperty("os.name").contains("windows", true)) {
-        commandLine("cmd", "/c", "npm", "run", "sassCompile")
+    dependsOn.add(tasks.getByName("ensureDirectory"))
+    if (System.getProperty("os.name").contains("windows",true)) {
+        commandLine("cmd", "/c", "sass", "src/main/resources/scss:src/main/resources/static/css")
     } else {
-        commandLine("npm", "run", "sassCompile")
+        commandLine("echo", "Checking for sass or sassc...")
+        doLast {
+            val res = exec {
+                isIgnoreExitValue = true
+                executable = "bash"
+                args = listOf("-l", "-c", "sass --version")
+            }
+            if (res.exitValue == 0) {
+                exec { commandLine("sass", "src/main/resources/scss:src/main/resources/static/css") }
+            } else {
+                File("src/main/resources/scss").listFiles()!!.filter { it.extension == "scss" && !it.name.startsWith("_") }.forEach {
+                    exec { commandLine("sassc", "src/main/resources/scss/${it.name}", "src/main/resources/static/css/${it.                        nameWithoutExtension}.css") }
+                }
+            }
+        }
     }
 }
+
 task<Exec>("tsCompile") {
     if (System.getProperty("os.name").contains("windows", true)) {
         commandLine("cmd", "/c", "npm", "run", "tsCompile")
@@ -231,7 +257,7 @@ dependencies {
     // DB Drivers / Migration
     implementation("org.liquibase:liquibase-core")
     implementation("com.h2database:h2")
-    implementation("mysql:mysql-connector-java")
+    implementation("com.mysql:mysql-connector-j")
     implementation("org.mariadb.jdbc:mariadb-java-client")
     implementation("org.postgresql:postgresql")
 
@@ -256,17 +282,15 @@ dependencies {
     implementation("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect")
 
     // Guava
-    implementation("com.google.guava:guava:31.0.1-jre")
+    implementation("com.google.guava:guava:$guavaVersion")
 
     // Modemapper
     implementation("org.modelmapper:modelmapper:2.4.5")
 
     // Webjars
     implementation("org.webjars:webjars-locator-core")
-    implementation("org.webjars:jquery:3.6.0")
+    implementation("org.webjars:jquery:3.7.1")
     implementation("org.webjars:js-cookie:2.2.1")
-    // Better DateTime handling in JavaScript
-    implementation("org.webjars.npm:luxon:2.3.2")
 
     //// Websockets
     //implementation("org.webjars:sockjs-client:1.5.1")
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ffed3a254e91df704a9acc0f2745c0e340d9b582..a59520664252cb0fcb9587b12a18e36e6aa70253 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 744e882ed57263a19bf3a504977da292d009345f..1b6c787337ffb79f0e3cf8b1e9f00f680a959de1 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
 
 #
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
 #
 
 ##############################################################################
-##
-##  Gradle start up script for UN*X
-##
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
 ##############################################################################
 
 # Attempt to set APP_HOME
+
 # Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-        PRG="$link"
-    else
-        PRG=`dirname "$PRG"`"/$link"
-    fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
 done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
 
 APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
 
 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
 
 warn () {
     echo "$*"
-}
+} >&2
 
 die () {
     echo
     echo "$*"
     echo
     exit 1
-}
+} >&2
 
 # OS specific support (must be 'true' or 'false').
 cygwin=false
 msys=false
 darwin=false
 nonstop=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MSYS* | MINGW* )
-    msys=true
-    ;;
-  NONSTOP* )
-    nonstop=true
-    ;;
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
 esac
 
 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 if [ -n "$JAVA_HOME" ] ; then
     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
         # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD="$JAVA_HOME/jre/sh/java"
+        JAVACMD=$JAVA_HOME/jre/sh/java
     else
-        JAVACMD="$JAVA_HOME/bin/java"
+        JAVACMD=$JAVA_HOME/bin/java
     fi
     if [ ! -x "$JAVACMD" ] ; then
         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
     fi
 else
-    JAVACMD="java"
+    JAVACMD=java
     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,95 @@ location of your Java installation."
 fi
 
 # Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
-    MAX_FD_LIMIT=`ulimit -H -n`
-    if [ $? -eq 0 ] ; then
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
-            MAX_FD="$MAX_FD_LIMIT"
-        fi
-        ulimit -n $MAX_FD
-        if [ $? -ne 0 ] ; then
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
-        fi
-    else
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
-    fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
 fi
 
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
-    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
 
 # For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
-    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
-    JAVACMD=`cygpath --unix "$JAVACMD"`
-
-    # We build the pattern for arguments to be converted via cygpath
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
-    SEP=""
-    for dir in $ROOTDIRSRAW ; do
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
-        SEP="|"
-    done
-    OURCYGPATTERN="(^($ROOTDIRS))"
-    # Add a user-defined pattern to the cygpath arguments
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
-    fi
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
     # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    i=0
-    for arg in "$@" ; do
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
-
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
-        else
-            eval `echo args$i`="\"$arg\""
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
         fi
-        i=`expr $i + 1`
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
     done
-    case $i in
-        0) set -- ;;
-        1) set -- "$args0" ;;
-        2) set -- "$args0" "$args1" ;;
-        3) set -- "$args0" "$args1" "$args2" ;;
-        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
 fi
 
-# Escape application args
-save () {
-    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
-    echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command;
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+#     shell script including quotes and variable substitutions, so put them in
+#     double quotes to make sure that they get re-expanded; and
+#   * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
 
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
 
 exec "$JAVACMD" "$@"
diff --git a/src/main/java/nl/tudelft/pupple/DevDatabaseLoader.java b/src/main/java/nl/tudelft/pupple/DevDatabaseLoader.java
index 0e67df9fa8c2b50d49a534d3fba61474e345f22c..595b37527b44ad57b7ff8c1c79fbafcf3a83db3d 100644
--- a/src/main/java/nl/tudelft/pupple/DevDatabaseLoader.java
+++ b/src/main/java/nl/tudelft/pupple/DevDatabaseLoader.java
@@ -24,6 +24,10 @@ import java.util.List;
 import javax.annotation.PostConstruct;
 import javax.transaction.Transactional;
 
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
 import nl.tudelft.labracore.api.CourseControllerApi;
 import nl.tudelft.labracore.api.EditionControllerApi;
 import nl.tudelft.labracore.api.RoleControllerApi;
@@ -40,10 +44,6 @@ import nl.tudelft.pupple.repository.PuzzleAttemptRepository;
 import nl.tudelft.pupple.repository.PuzzleRepository;
 import nl.tudelft.pupple.repository.labracore.PersonRepository;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Profile;
-import org.springframework.stereotype.Service;
-
 @Service
 @Profile("dev")
 @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") // Can be disabled as this class is not tested.
diff --git a/src/main/java/nl/tudelft/pupple/PuppleApplication.java b/src/main/java/nl/tudelft/pupple/PuppleApplication.java
index a324057ca698fd01927c1aba8c0afaf44671a183..935958ee176a69be2190a03fb6e9fabe1c01dbe5 100644
--- a/src/main/java/nl/tudelft/pupple/PuppleApplication.java
+++ b/src/main/java/nl/tudelft/pupple/PuppleApplication.java
@@ -17,13 +17,13 @@
  */
 package nl.tudelft.pupple;
 
-import nl.tudelft.labracore.lib.LabracoreApiConfig;
-import nl.tudelft.librador.EnableLibrador;
-
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.context.annotation.Import;
 
+import nl.tudelft.labracore.lib.LabracoreApiConfig;
+import nl.tudelft.librador.EnableLibrador;
+
 @EnableLibrador
 @Import(LabracoreApiConfig.class)
 @SpringBootApplication(scanBasePackageClasses = PuppleApplication.class)
diff --git a/src/main/java/nl/tudelft/pupple/cache/PersonCacheManager.java b/src/main/java/nl/tudelft/pupple/cache/PersonCacheManager.java
index 55438c93dfb95934761eb506eba870011540bfae..aed780071c5a25f686d4b77b3519fa22de54556f 100644
--- a/src/main/java/nl/tudelft/pupple/cache/PersonCacheManager.java
+++ b/src/main/java/nl/tudelft/pupple/cache/PersonCacheManager.java
@@ -21,15 +21,15 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
-import nl.tudelft.labracore.api.PersonControllerApi;
-import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
-
 import org.modelmapper.ModelMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.env.Environment;
 import org.springframework.stereotype.Component;
 import org.springframework.web.context.annotation.ApplicationScope;
 
+import nl.tudelft.labracore.api.PersonControllerApi;
+import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
+
 @Component
 @ApplicationScope
 public class PersonCacheManager extends TimedCacheManager<Long, PersonSummaryDTO> {
diff --git a/src/main/java/nl/tudelft/pupple/cache/RoleCacheManager.java b/src/main/java/nl/tudelft/pupple/cache/RoleCacheManager.java
index b40f926969d3df91c231ea1b49c4dd8941ff62a0..558903a7be36affc02ba4616ebdf6c265cb86269 100644
--- a/src/main/java/nl/tudelft/pupple/cache/RoleCacheManager.java
+++ b/src/main/java/nl/tudelft/pupple/cache/RoleCacheManager.java
@@ -20,14 +20,14 @@ package nl.tudelft.pupple.cache;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import nl.tudelft.labracore.api.RoleControllerApi;
-import nl.tudelft.labracore.api.dto.Id;
-import nl.tudelft.labracore.api.dto.RoleDetailsDTO;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.web.context.annotation.RequestScope;
 
+import nl.tudelft.labracore.api.RoleControllerApi;
+import nl.tudelft.labracore.api.dto.Id;
+import nl.tudelft.labracore.api.dto.RoleDetailsDTO;
+
 @Component
 @RequestScope
 public class RoleCacheManager extends CoreCacheManager<Id, RoleDetailsDTO> {
diff --git a/src/main/java/nl/tudelft/pupple/config/LibradorConfiguration.java b/src/main/java/nl/tudelft/pupple/config/LibradorConfiguration.java
index 00412106260a0688ced8e4a08cbe487550d73fd2..0cb40ef6b4b641e680d16a36bea0e707d08f17a4 100644
--- a/src/main/java/nl/tudelft/pupple/config/LibradorConfiguration.java
+++ b/src/main/java/nl/tudelft/pupple/config/LibradorConfiguration.java
@@ -19,14 +19,14 @@ package nl.tudelft.pupple.config;
 
 import static org.modelmapper.convention.MatchingStrategies.STRICT;
 
-import nl.tudelft.librador.EnableLibrador;
-import nl.tudelft.librador.LibradorConfigAdapter;
-import nl.tudelft.librador.dto.id.IdMapperBuilder;
-
 import org.modelmapper.ModelMapper;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+import nl.tudelft.librador.EnableLibrador;
+import nl.tudelft.librador.LibradorConfigAdapter;
+import nl.tudelft.librador.dto.id.IdMapperBuilder;
+
 @Configuration
 @EnableLibrador
 public class LibradorConfiguration extends LibradorConfigAdapter {
diff --git a/src/main/java/nl/tudelft/pupple/controller/HomeController.java b/src/main/java/nl/tudelft/pupple/controller/HomeController.java
index fe88caa21d0e18d915f340fec8d6acdfc3b4a69e..107912cf7a9275e46e32a2dc3b2f14bdc25d0bd1 100644
--- a/src/main/java/nl/tudelft/pupple/controller/HomeController.java
+++ b/src/main/java/nl/tudelft/pupple/controller/HomeController.java
@@ -19,6 +19,11 @@ package nl.tudelft.pupple.controller;
 
 import javax.transaction.Transactional;
 
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+
 import nl.tudelft.labracore.api.EditionControllerApi;
 import nl.tudelft.labracore.api.RoleControllerApi;
 import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
@@ -26,11 +31,6 @@ import nl.tudelft.labracore.lib.security.user.Person;
 import nl.tudelft.pupple.repository.PuzzleRepository;
 import nl.tudelft.pupple.repository.labracore.PersonRepository;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.GetMapping;
-
 @Controller
 public class HomeController {
 
diff --git a/src/main/java/nl/tudelft/pupple/controller/PersonController.java b/src/main/java/nl/tudelft/pupple/controller/PersonController.java
index 847cbd89ba2af4d2eaee5bce5d95182a8b16f8f3..f852dc96329af0e888e5f3a196cb8b0aa6364fe3 100644
--- a/src/main/java/nl/tudelft/pupple/controller/PersonController.java
+++ b/src/main/java/nl/tudelft/pupple/controller/PersonController.java
@@ -17,15 +17,13 @@
  */
 package nl.tudelft.pupple.controller;
 
-import nl.tudelft.labracore.api.PersonControllerApi;
-import nl.tudelft.labracore.api.dto.PersonDetailsDTO;
-import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
-import nl.tudelft.labracore.lib.security.user.Person;
-import nl.tudelft.pupple.model.labracore.PupplePerson;
-import nl.tudelft.pupple.repository.labracore.*;
-import nl.tudelft.pupple.security.AuthorisationService;
+import java.time.LocalDate;
+import java.util.*;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Controller;
@@ -33,19 +31,41 @@ import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.server.ResponseStatusException;
 
+import nl.tudelft.labracore.api.PersonControllerApi;
+import nl.tudelft.labracore.api.dto.PersonDetailsDTO;
+import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
+import nl.tudelft.labracore.lib.security.user.Person;
+import nl.tudelft.pupple.controller.utility.PageUtil;
+import nl.tudelft.pupple.model.Puzzle;
+import nl.tudelft.pupple.model.labracore.PupplePerson;
+import nl.tudelft.pupple.repository.PuzzleAttemptRepository;
+import nl.tudelft.pupple.repository.PuzzleRepository;
+import nl.tudelft.pupple.repository.labracore.PersonRepository;
+import nl.tudelft.pupple.security.AuthorisationService;
+import nl.tudelft.pupple.service.PuzzleAttemptService;
+
 @Controller
 @RequestMapping("profile")
 public class PersonController {
 	private final PersonRepository personRepository;
+	private final PuzzleRepository puzzleRepository;
+	private final PuzzleAttemptRepository puzzleAttemptRepository;
 	private final AuthorisationService authorisationService;
+	private final PuzzleAttemptService puzzleAttemptService;
 	private final PersonControllerApi personControllerApi;
 
 	@Autowired
 	public PersonController(PersonRepository personRepository,
+			PuzzleRepository puzzleRepository,
+			PuzzleAttemptRepository puzzleAttemptRepository,
 			AuthorisationService authorisationService,
+			PuzzleAttemptService puzzleAttemptService,
 			PersonControllerApi personControllerApi) {
 		this.personRepository = personRepository;
+		this.puzzleRepository = puzzleRepository;
+		this.puzzleAttemptRepository = puzzleAttemptRepository;
 		this.authorisationService = authorisationService;
+		this.puzzleAttemptService = puzzleAttemptService;
 		this.personControllerApi = personControllerApi;
 	}
 
@@ -57,18 +77,67 @@ public class PersonController {
 
 	@GetMapping("{id}")
 	@PreAuthorize("@authorisationService.isAuthenticated()")
-	public String getProfile(@AuthenticatedPerson Person person, @PathVariable("id") long personId,
+	public String getProfile(@AuthenticatedPerson Person person,
+			@PageableDefault(value = 7) Pageable pageable,
+			@PathVariable("id") long personId,
+			@RequestParam(name = "unassigned", required = false) String unassigned,
 			Model model) {
 		PupplePerson pupplePerson = personRepository.findByIdOrThrow(personId);
-		if (authorisationService.isAdmin() || person.getId() == personId || pupplePerson.getPublicProfile()) {
-			PersonDetailsDTO otherPerson = personControllerApi.getPersonById(personId).block();
+		if (!(authorisationService.isAdmin() || person.getId() == personId
+				|| pupplePerson.getPublicProfile())) {
+			return "profile/forbidden";
+		}
+
+		PersonDetailsDTO otherPerson = personControllerApi.getPersonById(personId).block();
 
-			model.addAttribute("displayName", otherPerson.getDisplayName());
-			model.addAttribute("pupplePerson", pupplePerson);
-			model.addAttribute("isOwner", person.getId() == personId);
-			return "profile/view";
+		List<Puzzle> puzzlesFeatured;
+		boolean isOwner = person.getId() == personId;
+		boolean canViewFuturePuzzles = isOwner || authorisationService.isAdmin();
+
+		if (unassigned != null) {
+			if (canViewFuturePuzzles) {
+				puzzlesFeatured = puzzleRepository.findByDayUsedIsNullAndCreator(pupplePerson);
+			} else {
+				puzzlesFeatured = new ArrayList<>();
+			}
+		} else {
+			if (canViewFuturePuzzles) {
+				puzzlesFeatured = puzzleRepository.findByCreator(pupplePerson);
+			} else {
+				puzzlesFeatured = puzzleRepository.findByDayUsedIsBeforeAndCreator(LocalDate
+						.now().plusDays(1), pupplePerson);
+			}
 		}
-		return "profile/forbidden";
+
+		HashMap<Long, String> displayNames = new HashMap<>();
+		puzzlesFeatured.stream()
+				.filter(puzzle -> puzzle.getCreator() != null && puzzle.getCreator().getPublicProfile())
+				.map(puzzle -> puzzle.getCreator().getId())
+				.forEach((id) -> displayNames.put(id,
+						personControllerApi.getPersonById(id).block().getDisplayName()));
+		HashMap<Long, Long> attempts = new HashMap<>();
+		puzzlesFeatured.stream()
+				.filter(puzzle -> puzzleAttemptRepository.getByPersonIdAndPuzzleId(person.getId(),
+						puzzle.getId()) != null)
+				.forEach((puzzle) -> attempts.put(puzzle.getId(),
+						(long) puzzleAttemptRepository.getByPersonIdAndPuzzleId(
+								person.getId(), puzzle.getId()).getGuesses().size()));
+
+		puzzlesFeatured.sort(Comparator
+				.comparing(Puzzle::getDayUsed, Comparator.nullsFirst(Comparator.naturalOrder()))
+				.reversed());
+		Page<Puzzle> puzzlePage = PageUtil.pageFromList(puzzlesFeatured, pageable);
+		Set<Puzzle> puzzlesSolved = new HashSet<>(puzzleAttemptService
+				.filterSolvedPuzzles(person, puzzlesFeatured));
+
+		model.addAttribute("displayName", otherPerson.getDisplayName());
+		model.addAttribute("pupplePerson", pupplePerson);
+		model.addAttribute("solved", puzzlesSolved);
+		model.addAttribute("puzzles", puzzlePage);
+		model.addAttribute("isOwner", isOwner);
+		model.addAttribute("displayNames", displayNames);
+		model.addAttribute("guessAmounts", attempts);
+		return "profile/view";
 	}
 
 	@PostMapping("{id}")
diff --git a/src/main/java/nl/tudelft/pupple/controller/PuzzleController.java b/src/main/java/nl/tudelft/pupple/controller/PuzzleController.java
index e3656e83441d2a71c259806fda3af7586b0086f7..2577f866ffb860f640dc916b7c78faf63f62aadd 100644
--- a/src/main/java/nl/tudelft/pupple/controller/PuzzleController.java
+++ b/src/main/java/nl/tudelft/pupple/controller/PuzzleController.java
@@ -23,6 +23,17 @@ import java.util.stream.Collectors;
 
 import javax.transaction.Transactional;
 
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.server.ResponseStatusException;
+
 import nl.tudelft.labracore.api.PersonControllerApi;
 import nl.tudelft.labracore.api.dto.PersonDetailsDTO;
 import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
@@ -43,17 +54,6 @@ import nl.tudelft.pupple.security.AuthorisationService;
 import nl.tudelft.pupple.service.PuzzleAttemptService;
 import nl.tudelft.pupple.service.PuzzleService;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.web.PageableDefault;
-import org.springframework.http.HttpStatus;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.server.ResponseStatusException;
-
 @Controller
 @RequestMapping("puzzle")
 public class PuzzleController {
@@ -112,12 +112,27 @@ public class PuzzleController {
 						pupplePerson);
 			}
 		}
+		HashMap<Long, String> displayNames = new HashMap<>();
+		puzzles.stream()
+				.filter(puzzle -> puzzle.getCreator() != null && puzzle.getCreator().getPublicProfile())
+				.map(puzzle -> puzzle.getCreator().getId())
+				.forEach((id) -> displayNames.put(id,
+						personControllerApi.getPersonById(id).block().getDisplayName()));
+		HashMap<Long, Long> attempts = new HashMap<>();
+		puzzles.stream()
+				.filter(puzzle -> puzzleAttemptRepository.getByPersonIdAndPuzzleId(person.getId(),
+						puzzle.getId()) != null)
+				.forEach((puzzle) -> attempts.put(puzzle.getId(),
+						(long) puzzleAttemptRepository.getByPersonIdAndPuzzleId(
+								person.getId(), puzzle.getId()).getGuesses().size()));
 		Collections.sort(puzzles, Comparator
 				.comparing(Puzzle::getDayUsed, Comparator.nullsFirst(Comparator.naturalOrder())).reversed());
 		Set<Puzzle> puzzlesSolved = new HashSet<>(puzzleAttemptService.filterSolvedPuzzles(person, puzzles));
 		Page<Puzzle> pageOfPuzzles = PageUtil.pageFromList(puzzles, pageable);
 		model.addAttribute("puzzles", pageOfPuzzles);
 		model.addAttribute("solved", puzzlesSolved);
+		model.addAttribute("displayNames", displayNames);
+		model.addAttribute("guessAmounts", attempts);
 		return "puzzle/all";
 	}
 
diff --git a/src/main/java/nl/tudelft/pupple/dto/patch/PuzzlePatchDTO.java b/src/main/java/nl/tudelft/pupple/dto/patch/PuzzlePatchDTO.java
index 1ebbfcaae61f73813fc04329818956762144a9c1..244e78d2076397e0f3c8ebe568154894ce39f63c 100644
--- a/src/main/java/nl/tudelft/pupple/dto/patch/PuzzlePatchDTO.java
+++ b/src/main/java/nl/tudelft/pupple/dto/patch/PuzzlePatchDTO.java
@@ -25,12 +25,12 @@ import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
 
+import org.springframework.format.annotation.DateTimeFormat;
+
 import lombok.*;
 import nl.tudelft.librador.dto.patch.Patch;
 import nl.tudelft.pupple.model.Puzzle;
 
-import org.springframework.format.annotation.DateTimeFormat;
-
 @Data
 @Builder
 @NoArgsConstructor
diff --git a/src/main/java/nl/tudelft/pupple/model/Puzzle.java b/src/main/java/nl/tudelft/pupple/model/Puzzle.java
index dface6934271c429fb7a1a820f1596bcb814277c..9c14ae1056c90173fb4818223290f704fc467ef9 100644
--- a/src/main/java/nl/tudelft/pupple/model/Puzzle.java
+++ b/src/main/java/nl/tudelft/pupple/model/Puzzle.java
@@ -25,12 +25,12 @@ import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
 
-import lombok.*;
-import nl.tudelft.pupple.model.labracore.PupplePerson;
-
 import org.hibernate.annotations.Cascade;
 import org.hibernate.annotations.CascadeType;
 
+import lombok.*;
+import nl.tudelft.pupple.model.labracore.PupplePerson;
+
 @Data
 @Entity
 @Builder
diff --git a/src/main/java/nl/tudelft/pupple/model/PuzzleAttempt.java b/src/main/java/nl/tudelft/pupple/model/PuzzleAttempt.java
index 9eb6d6d870c971b71ceeec5a958f8b688400995e..cfcf1fca481a99b926e7c4fa8e9f968727f54b4d 100644
--- a/src/main/java/nl/tudelft/pupple/model/PuzzleAttempt.java
+++ b/src/main/java/nl/tudelft/pupple/model/PuzzleAttempt.java
@@ -23,12 +23,12 @@ import java.util.List;
 import javax.persistence.*;
 import javax.validation.constraints.NotNull;
 
-import lombok.*;
-import nl.tudelft.pupple.model.labracore.PupplePerson;
-
 import org.hibernate.annotations.Cascade;
 import org.hibernate.annotations.CascadeType;
 
+import lombok.*;
+import nl.tudelft.pupple.model.labracore.PupplePerson;
+
 @Data
 @Entity
 @Builder
diff --git a/src/main/java/nl/tudelft/pupple/repository/GuessRepository.java b/src/main/java/nl/tudelft/pupple/repository/GuessRepository.java
index 2f0afe773d7a1f4adec4bce2e02d51c68cac8a15..d41e6e078bfc87ce7b2366e4fecb2f52fc13fe11 100644
--- a/src/main/java/nl/tudelft/pupple/repository/GuessRepository.java
+++ b/src/main/java/nl/tudelft/pupple/repository/GuessRepository.java
@@ -20,11 +20,11 @@ package nl.tudelft.pupple.repository;
 import java.util.Collection;
 import java.util.List;
 
-import nl.tudelft.pupple.model.Guess;
-
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.web.client.ResourceAccessException;
 
+import nl.tudelft.pupple.model.Guess;
+
 public interface GuessRepository extends JpaRepository<Guess, Long> {
 	default Guess findByIdOrThrow(long id) {
 		return findById(id).orElseThrow(() -> new ResourceAccessException("Guess was not found: " + id));
diff --git a/src/main/java/nl/tudelft/pupple/repository/HintRepository.java b/src/main/java/nl/tudelft/pupple/repository/HintRepository.java
index 70bc3d6cb1287171c11d48b9e1e40880a301685d..3516e771560e98f435101e94302bc9f51c074c83 100644
--- a/src/main/java/nl/tudelft/pupple/repository/HintRepository.java
+++ b/src/main/java/nl/tudelft/pupple/repository/HintRepository.java
@@ -20,11 +20,11 @@ package nl.tudelft.pupple.repository;
 import java.util.Collection;
 import java.util.List;
 
-import nl.tudelft.pupple.model.Hint;
-
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.web.client.ResourceAccessException;
 
+import nl.tudelft.pupple.model.Hint;
+
 public interface HintRepository extends JpaRepository<Hint, Long> {
 	default Hint findByIdOrThrow(long id) {
 		return findById(id).orElseThrow(() -> new ResourceAccessException("Hint was not found: " + id));
diff --git a/src/main/java/nl/tudelft/pupple/repository/PuzzleAttemptRepository.java b/src/main/java/nl/tudelft/pupple/repository/PuzzleAttemptRepository.java
index d68f2eaa444a1348e198c524e17f450529b6d5e7..d89e5778d44567f25eda56bd6801b3b52b62dd3b 100644
--- a/src/main/java/nl/tudelft/pupple/repository/PuzzleAttemptRepository.java
+++ b/src/main/java/nl/tudelft/pupple/repository/PuzzleAttemptRepository.java
@@ -20,11 +20,11 @@ package nl.tudelft.pupple.repository;
 import java.util.List;
 import java.util.Set;
 
-import nl.tudelft.pupple.model.PuzzleAttempt;
-
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.web.client.ResourceAccessException;
 
+import nl.tudelft.pupple.model.PuzzleAttempt;
+
 public interface PuzzleAttemptRepository extends JpaRepository<PuzzleAttempt, Long> {
 	default PuzzleAttempt findByIdOrThrow(long id) {
 		return findById(id)
diff --git a/src/main/java/nl/tudelft/pupple/repository/PuzzleRepository.java b/src/main/java/nl/tudelft/pupple/repository/PuzzleRepository.java
index 72157054d2f02390b5c43f9c89aef6e0c02257e6..15463fee15ca7c78f7f613da047100a98c57010d 100644
--- a/src/main/java/nl/tudelft/pupple/repository/PuzzleRepository.java
+++ b/src/main/java/nl/tudelft/pupple/repository/PuzzleRepository.java
@@ -21,23 +21,31 @@ import java.time.LocalDate;
 import java.util.Collection;
 import java.util.List;
 
-import nl.tudelft.pupple.model.Puzzle;
-import nl.tudelft.pupple.model.labracore.PupplePerson;
-
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.web.client.ResourceAccessException;
 
+import nl.tudelft.pupple.model.Puzzle;
+import nl.tudelft.pupple.model.labracore.PupplePerson;
+
 public interface PuzzleRepository extends JpaRepository<Puzzle, Long> {
 	default Puzzle findByIdOrThrow(long id) {
 		return findById(id).orElseThrow(() -> new ResourceAccessException("Puzzle was not found: " + id));
 	}
 
+	long countByCreator(PupplePerson creator);
+
+	long countByDayUsedIsBeforeAndCreator(LocalDate date, PupplePerson creator);
+
 	List<Puzzle> findAllByIdIn(Collection<Long> id);
 
+	List<Puzzle> findByCreator(PupplePerson creator);
+
 	Puzzle findByDayUsed(LocalDate date);
 
 	List<Puzzle> findByDayUsedIsNotNullAndDayUsedIsBefore(LocalDate date);
 
+	List<Puzzle> findByDayUsedIsBeforeAndCreator(LocalDate date, PupplePerson creator);
+
 	List<Puzzle> findByDayUsedIsBeforeOrCreator(LocalDate date, PupplePerson creator);
 
 	List<Puzzle> findByDayUsedIsNull();
diff --git a/src/main/java/nl/tudelft/pupple/repository/labracore/PersonRepository.java b/src/main/java/nl/tudelft/pupple/repository/labracore/PersonRepository.java
index 54e4ea2248db91cda043df7b3b9920ad96f167f3..f2dcde756777c21c022ce68fc4d276bf686e9873 100644
--- a/src/main/java/nl/tudelft/pupple/repository/labracore/PersonRepository.java
+++ b/src/main/java/nl/tudelft/pupple/repository/labracore/PersonRepository.java
@@ -17,11 +17,11 @@
  */
 package nl.tudelft.pupple.repository.labracore;
 
-import nl.tudelft.pupple.model.labracore.PupplePerson;
-
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.rest.webmvc.ResourceNotFoundException;
 
+import nl.tudelft.pupple.model.labracore.PupplePerson;
+
 public interface PersonRepository extends JpaRepository<PupplePerson, Long> {
 
 	default PupplePerson findByIdOrThrow(Long id) {
diff --git a/src/main/java/nl/tudelft/pupple/security/AuthorisationService.java b/src/main/java/nl/tudelft/pupple/security/AuthorisationService.java
index 4de96e97beb3b8f9ace26e059c5344f6c45f4ffc..4a2a9d4bec59370669cbaba9580422a5b098d019 100644
--- a/src/main/java/nl/tudelft/pupple/security/AuthorisationService.java
+++ b/src/main/java/nl/tudelft/pupple/security/AuthorisationService.java
@@ -19,16 +19,17 @@ package nl.tudelft.pupple.security;
 
 import java.time.LocalDate;
 
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
 import nl.tudelft.labracore.api.CourseControllerApi;
 import nl.tudelft.labracore.lib.security.LabradorUserDetails;
 import nl.tudelft.labracore.lib.security.user.DefaultRole;
 import nl.tudelft.labracore.lib.security.user.Person;
 import nl.tudelft.pupple.cache.RoleCacheManager;
 import nl.tudelft.pupple.model.Puzzle;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.stereotype.Service;
+import nl.tudelft.pupple.model.labracore.PupplePerson;
 
 @Service
 public class AuthorisationService {
@@ -111,6 +112,16 @@ public class AuthorisationService {
 		return false;
 	}
 
+	public boolean canEditPuzzlesOfUser(PupplePerson user) {
+		if (!isAuthenticated()) {
+			return false;
+		}
+		if (isAdmin()) {
+			return true;
+		}
+		return user != null && user.getId().equals(getAuthPerson().getId());
+	}
+
 	public boolean canViewPuzzle(Puzzle puzzle) {
 		if (!isAuthenticated()) {
 			return false;
diff --git a/src/main/java/nl/tudelft/pupple/security/LoginSecurityConfigurerAdapter.java b/src/main/java/nl/tudelft/pupple/security/LoginSecurityConfigurerAdapter.java
index 8aade5bb8168a397153822ed2f96b1375e8de0fc..80f8eeb2ed4c4d357645066b2c4f17d218b085bd 100644
--- a/src/main/java/nl/tudelft/pupple/security/LoginSecurityConfigurerAdapter.java
+++ b/src/main/java/nl/tudelft/pupple/security/LoginSecurityConfigurerAdapter.java
@@ -17,14 +17,14 @@
  */
 package nl.tudelft.pupple.security;
 
-import nl.tudelft.labracore.lib.security.LabradorSecurityConfigurerAdapter;
-
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.Order;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 
+import nl.tudelft.labracore.lib.security.LabradorSecurityConfigurerAdapter;
+
 /**
  * Security configuration adapter required to make use of the LabraDoor login.
  *
diff --git a/src/main/java/nl/tudelft/pupple/security/LoginUserHandler.java b/src/main/java/nl/tudelft/pupple/security/LoginUserHandler.java
index a2dbafb16cfc6f04b5560d6fb803c0d056deaf5f..aa497e758d4c68a25d906c491c254907eb0eb4b8 100644
--- a/src/main/java/nl/tudelft/pupple/security/LoginUserHandler.java
+++ b/src/main/java/nl/tudelft/pupple/security/LoginUserHandler.java
@@ -17,15 +17,14 @@
  */
 package nl.tudelft.pupple.security;
 
-import nl.tudelft.labracore.lib.security.LabradorUserHandler;
-import nl.tudelft.labracore.lib.security.user.Person;
-import nl.tudelft.pupple.model.labracore.PupplePerson;
-import nl.tudelft.pupple.repository.labracore.PersonRepository;
-
 import org.springframework.stereotype.Service;
 
 import io.sentry.Sentry;
 import io.sentry.protocol.User;
+import nl.tudelft.labracore.lib.security.LabradorUserHandler;
+import nl.tudelft.labracore.lib.security.user.Person;
+import nl.tudelft.pupple.model.labracore.PupplePerson;
+import nl.tudelft.pupple.repository.labracore.PersonRepository;
 
 /**
  * Interface for handling user logins on the client implementation.
diff --git a/src/main/java/nl/tudelft/pupple/security/LoginUserProvider.java b/src/main/java/nl/tudelft/pupple/security/LoginUserProvider.java
index 30bd0cd547577dd09f9b311917ea069f59ae4264..30ec87985639a5f99f4d228b0168ab78cd0c3ac7 100644
--- a/src/main/java/nl/tudelft/pupple/security/LoginUserProvider.java
+++ b/src/main/java/nl/tudelft/pupple/security/LoginUserProvider.java
@@ -19,11 +19,11 @@ package nl.tudelft.pupple.security;
 
 import static nl.tudelft.labracore.lib.security.user.DefaultRole.*;
 
-import nl.tudelft.labracore.lib.security.memory.InMemoryUserProvider;
-
 import org.springframework.context.annotation.Profile;
 import org.springframework.stereotype.Service;
 
+import nl.tudelft.labracore.lib.security.memory.InMemoryUserProvider;
+
 /**
  * Provider for in-memory user information.
  *
diff --git a/src/main/java/nl/tudelft/pupple/service/PuzzleAttemptService.java b/src/main/java/nl/tudelft/pupple/service/PuzzleAttemptService.java
index 77e56a01a83efbe3489f6cedbe7fade7d30a34a3..e25781f6701a2b096d390d71791ca83aa8796082 100644
--- a/src/main/java/nl/tudelft/pupple/service/PuzzleAttemptService.java
+++ b/src/main/java/nl/tudelft/pupple/service/PuzzleAttemptService.java
@@ -19,6 +19,11 @@ package nl.tudelft.pupple.service;
 
 import java.util.List;
 
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+import org.springframework.web.server.ResponseStatusException;
+
 import nl.tudelft.labracore.lib.security.user.Person;
 import nl.tudelft.pupple.model.Guess;
 import nl.tudelft.pupple.model.Puzzle;
@@ -30,11 +35,6 @@ import nl.tudelft.pupple.repository.PuzzleRepository;
 import nl.tudelft.pupple.repository.labracore.PersonRepository;
 import nl.tudelft.pupple.security.AuthorisationService;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.stereotype.Service;
-import org.springframework.web.server.ResponseStatusException;
-
 @Service
 public class PuzzleAttemptService {
 	private final GuessRepository guessRepository;
diff --git a/src/main/java/nl/tudelft/pupple/service/PuzzleService.java b/src/main/java/nl/tudelft/pupple/service/PuzzleService.java
index e4dea73e3b122c105f4d50b6bfc4248d4797fe4f..778a698cb14ed987d81f07df1805c51076933208 100644
--- a/src/main/java/nl/tudelft/pupple/service/PuzzleService.java
+++ b/src/main/java/nl/tudelft/pupple/service/PuzzleService.java
@@ -19,12 +19,12 @@ package nl.tudelft.pupple.service;
 
 import java.time.LocalDate;
 
-import nl.tudelft.pupple.model.Puzzle;
-import nl.tudelft.pupple.repository.PuzzleRepository;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import nl.tudelft.pupple.model.Puzzle;
+import nl.tudelft.pupple.repository.PuzzleRepository;
+
 @Service
 public class PuzzleService {
 	private final PuzzleRepository puzzleRepository;
diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html
index 76b8a130ea3a57224f781bd2052a8273365b2061..d5588185a31ac3d31004411af3152ec0bfd3a053 100644
--- a/src/main/resources/templates/layout.html
+++ b/src/main/resources/templates/layout.html
@@ -27,10 +27,9 @@
 
         <script src="/webjars/jquery/jquery.min.js" defer></script>
         <script src="/webjars/js-cookie/js.cookie.js"></script>
-        <script src="/webjars/luxon/build/global/luxon.js"></script>
 
         <link rel="stylesheet" href="/webjars/chihuahui/main.css" />
-        <script src="/webjars/chihuahui/1.0.0/theme.js"></script>
+        <script src="/webjars/chihuahui/1.1.0/theme.js"></script>
 
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
         <meta name="viewport" content="width=device-width initial-scale=1" />
diff --git a/src/main/resources/templates/privacy.html b/src/main/resources/templates/privacy.html
index 7d641e6792129e0412d2919bb9852616de96915e..99e9c4b11df015b910e9d7d7a3b5997eb814f2ea 100644
--- a/src/main/resources/templates/privacy.html
+++ b/src/main/resources/templates/privacy.html
@@ -59,10 +59,8 @@
             <ul>
                 <li>Name</li>
                 <li>Email</li>
-                <li>Student number</li>
                 <li>NetID</li>
                 <li>affiliation (if the user is a student or teacher)</li>
-                <li>which department/faculty the user is part of</li>
             </ul>
             <p>
                 Furthermore, through their interactions with the system, we store the following data
diff --git a/src/main/resources/templates/profile/view.html b/src/main/resources/templates/profile/view.html
index d7589ba1cd0f61fdf3989fd660af2ab4300ea7c7..e50eab10332ede1bf5b0a93b65ac930b9e4f881f 100644
--- a/src/main/resources/templates/profile/view.html
+++ b/src/main/resources/templates/profile/view.html
@@ -31,16 +31,6 @@
         <div layout:fragment="content">
             <h1 class="font-800 mb-5" th:text="${displayName}" />
 
-            <table class="table">
-                <tr>
-                    <td>Number of puzzles solved:</td>
-                    <td th:text="${pupplePerson.numberOfPuzzlesSolved()}"></td>
-                </tr>
-                <tr>
-                    <td>Number of puzzles featured:</td>
-                    <td th:text="${pupplePerson.numberOfPuzzlesFeatured()}"></td>
-                </tr>
-            </table>
             <form
                 th:if="${isOwner}"
                 action="#"
@@ -54,6 +44,24 @@
                     th:checked="${pupplePerson.getPublicProfile()}" />
                 <button type="submit" class="button">Save</button>
             </form>
+            <table class="table">
+                <tr>
+                    <td>Number of puzzles solved:</td>
+                    <td th:text="${pupplePerson.numberOfPuzzlesSolved()}"></td>
+                </tr>
+                <tr>
+                    <td>Number of puzzles featured:</td>
+                    <td th:text="${pupplePerson.numberOfPuzzlesFeatured()}"></td>
+                </tr>
+                <tr>
+                    <td>Puzzles featured:</td>
+                </tr>
+            </table>
+
+            <div class="pl-4">
+                <th:block
+                    layout:replace="~{puzzleList :: puzzleList(puzzles=${puzzles}, solved=${solved}, user=${pupplePerson})}"></th:block>
+            </div>
         </div>
     </body>
 </html>
diff --git a/src/main/resources/templates/puzzle/all.html b/src/main/resources/templates/puzzle/all.html
index 6fa16452630e2f61a3f230cf1761945d0a48f98e..28082797c159f0208b60df63610f1cff2261acba 100644
--- a/src/main/resources/templates/puzzle/all.html
+++ b/src/main/resources/templates/puzzle/all.html
@@ -31,59 +31,8 @@
         <div layout:fragment="content">
             <h1 class="font-800 mb-5">All puzzles</h1>
 
-            <table class="table" data-style="surface">
-                <tr class="table__header">
-                    <th>Day</th>
-                    <th>Solved?</th>
-                    <th:block
-                        th:if="${@authorisationService.canAssignToDay() and param.showSolution != null}">
-                        <th>Solution</th>
-                    </th:block>
-                </tr>
-                <th:block th:each="puzzle : ${puzzles}">
-                    <tr>
-                        <td>
-                            <a
-                                th:href="@{/puzzle/{id}/(id=${puzzle.id})}"
-                                th:text="${puzzle.getDayUsed() == null ? 'Puzzle not used yet, id: ' + puzzle.id : puzzle.getDayUsed()}" />
-                        </td>
-                        <td>
-                            <span
-                                class="fa-solid"
-                                th:classappend="${solved.contains(puzzle) ? 'fa-square-check colour-accept' : 'fa-square-xmark colour-error'}" />
-                        </td>
-                        <th:block
-                            th:if="${@authorisationService.canAssignToDay() and param.showSolution != null}">
-                            <td>
-                                <span th:text="${puzzle.getSolution()}"></span>
-                            </td>
-                        </th:block>
-                    </tr>
-                </th:block>
-            </table>
             <th:block
-                layout:replace="~{pagination :: pagination(page=${puzzles}, size=5)}"></th:block>
-
-            <th-block th:if="${param.unassigned == null}">
-                <a class="link" href="?unassigned">
-                    <button class="button">Show unassigned puzzles</button>
-                </a>
-            </th-block>
-            <th-block th:if="${param.unassigned != null}">
-                <a class="link" href="?">
-                    <button class="button">Show all puzzles</button>
-                </a>
-            </th-block>
-            <th-block th:if="${@authorisationService.canAssignToDay()}">
-                <th-block th:if="${param.showSolution == null}">
-                    <a class="link" href="?showSolution">
-                        <button class="button">Show solutions</button>
-                    </a>
-                </th-block>
-                <th-block th:if="${param.showSolution != null}">
-                    <a class="link" href="?"><button class="button">Hide solutions</button></a>
-                </th-block>
-            </th-block>
+                layout:replace="~{puzzleList :: puzzleList(puzzles=${puzzles}, solved=${solved}, user=${null})}"></th:block>
         </div>
     </body>
 </html>
diff --git a/src/main/resources/templates/puzzle/attempts.html b/src/main/resources/templates/puzzle/attempts.html
index e285732da8e5ed11d411263f94a63eced5193abc..9841501092cfd87b985f4601e930845bfe8f5dc7 100644
--- a/src/main/resources/templates/puzzle/attempts.html
+++ b/src/main/resources/templates/puzzle/attempts.html
@@ -48,8 +48,10 @@
                 <th:block th:each="attempt : ${attempts}">
                     <tr>
                         <td>
-                            <span
-                                th:text="${people.get(attempt.getPerson().getId()).getDisplayName()}"></span>
+                            <a th:href="@{/profile/{id}(id=${attempt.getPerson().getId()})}">
+                                <span
+                                    th:text="${people.get(attempt.getPerson().getId()).getDisplayName()}"></span>
+                            </a>
                         </td>
                         <td>
                             <span th:text="${attempt.getGuesses().size()}"></span>
diff --git a/src/main/resources/templates/puzzle/view.html b/src/main/resources/templates/puzzle/view.html
index 7c71b06c6c9dfba8530655df180221659d8b7e3f..c18ee48afe29a22d64c273393992b86e726ace80 100644
--- a/src/main/resources/templates/puzzle/view.html
+++ b/src/main/resources/templates/puzzle/view.html
@@ -48,6 +48,11 @@
                         th:text="${puzzleCreator}"></a>
                 </h2>
             </th:block>
+            <th:block th:if="${@authorisationService.isAdmin()}">
+                <a class="link" th:href="@{/puzzle/{id}/attempts(id=${puzzle.getId()})}">
+                    View attempts
+                </a>
+            </th:block>
             <div class="grid col-2 font-800 align-center justify-center mb-5">
                 <th:block th:each="hint: ${puzzle.hints}">
                     <div class="hint align-center flex justify-center center-text p-5">
diff --git a/src/main/resources/templates/puzzleList.html b/src/main/resources/templates/puzzleList.html
new file mode 100644
index 0000000000000000000000000000000000000000..dbc23497f9799de7f6fd0a7e6814a25703534d25
--- /dev/null
+++ b/src/main/resources/templates/puzzleList.html
@@ -0,0 +1,108 @@
+<!--
+
+    Pupple
+    Copyright (C) 2023 - Delft University of Technology
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html>
+<html
+    lang="en"
+    xmlns:th="http://www.thymeleaf.org"
+    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
+    <div layout:fragment="puzzleList(puzzles, solved, user)">
+        <table class="table" data-style="surface">
+            <tr th:if="${puzzles.getNumberOfElements() == 0}" class="table__header">
+                <th colspan="2">No puzzles to show</th>
+            </tr>
+            <tr th:if="${puzzles.getNumberOfElements() > 0}" class="table__header">
+                <th>✓</th>
+                <th>Day</th>
+                <th>Creator</th>
+                <th>Tries</th>
+                <th:block
+                    th:if="${@authorisationService.canAssignToDay() and param.showSolution != null}">
+                    <th>Solution</th>
+                </th:block>
+            </tr>
+            <th:block th:each="puzzle : ${puzzles}">
+                <tr>
+                    <td style="text-align: right">
+                        <!--Solved?-->
+                        <span
+                            style="font-size: 21px"
+                            class="fa-solid"
+                            th:classappend="${solved.contains(puzzle) ? 'fa-square-check colour-accept' : 'fa-square-xmark colour-error'}" />
+                    </td>
+                    <td>
+                        <!--Day-->
+                        <a
+                            th:href="@{/puzzle/{id}/(id=${puzzle.id})}"
+                            th:text="${puzzle.getDayUsed() == null ? 'Puzzle not used yet, id: ' + puzzle.id : puzzle.getDayUsed()}" />
+                    </td>
+                    <td>
+                        <!--Creator-->
+                        <th:block
+                            th:if="${puzzle.getCreator() != null && puzzle.getCreator().getPublicProfile()}">
+                            <a
+                                th:href="@{/profile/{id}/(id=${puzzle.getCreator().getId()})}"
+                                th:text="${displayNames.get(puzzle.getCreator().getId())}" />
+                        </th:block>
+                        <th:block
+                            th:unless="${puzzle.getCreator() != null && puzzle.getCreator().getPublicProfile()}">
+                            <span th:text="Anonymous" style="font-style: italic" />
+                        </th:block>
+                    </td>
+                    <td style="text-align: center">
+                        <!--Guesses-->
+                        <th:block th:if="${guessAmounts.get(puzzle.getId()) > 0}">
+                            <span th:text="${guessAmounts.get(puzzle.getId())}" />
+                        </th:block>
+                    </td>
+                    <th:block
+                        th:if="${@authorisationService.canAssignToDay() and param.showSolution != null}">
+                        <td>
+                            <span th:text="${puzzle.getSolution()}"></span>
+                        </td>
+                    </th:block>
+                </tr>
+            </th:block>
+        </table>
+        <th-block
+            th:if="${@authorisationService.canAssignToDay() || @authorisationService.canEditPuzzlesOfUser(user)}">
+            <th:block
+                layout:replace="~{pagination :: pagination(page=${puzzles}, size=5)}"></th:block>
+
+            <th-block th:if="${param.unassigned == null}">
+                <a class="link" href="?unassigned">
+                    <button class="button">Show unassigned puzzles</button>
+                </a>
+            </th-block>
+            <th-block th:if="${param.unassigned != null}">
+                <a class="link" href="?">
+                    <button class="button">Show all puzzles</button>
+                </a>
+            </th-block>
+            <th-block th:if="${param.showSolution == null}">
+                <a class="link" href="?showSolution">
+                    <button class="button">Show solutions</button>
+                </a>
+            </th-block>
+            <th-block th:if="${param.showSolution != null}">
+                <a class="link" href="?"><button class="button">Hide solutions</button></a>
+            </th-block>
+        </th-block>
+    </div>
+</html>