Compare commits
2 Commits
0c61fa29cd
...
c78665b374
Author | SHA1 | Date | |
---|---|---|---|
c78665b374 | |||
8733b835b1 |
@ -564,18 +564,6 @@ angular.module("FICApp")
|
||||
})
|
||||
|
||||
.controller("AllTeamAssociationsController", function($scope, $http) {
|
||||
$scope.newdqa = "";
|
||||
$scope.addDelegatedQA = function () {
|
||||
if ($scope.newdqa.length) {
|
||||
if (!$scope.config.delegated_qa)
|
||||
$scope.config.delegated_qa = [];
|
||||
|
||||
$scope.config.delegated_qa.push($scope.newdqa);
|
||||
$scope.saveSettings();
|
||||
$scope.newdqa = "";
|
||||
}
|
||||
}
|
||||
|
||||
$scope.allAssociations = [];
|
||||
$http.get("api/teams-associations.json").then(function(response) {
|
||||
$scope.allAssociations = response.data;
|
||||
@ -637,16 +625,28 @@ angular.module("FICApp")
|
||||
$scope.config.disablesubmitbutton = "";
|
||||
};
|
||||
|
||||
$scope.dropDelegatedQA = function (member) {
|
||||
if (!$scope.config.delegated_qa) {
|
||||
$scope.newdqa = "";
|
||||
$scope.addDelegatedQA = function() {
|
||||
if ($scope.newdqa.length) {
|
||||
if (!$scope.config.delegated_qa)
|
||||
$scope.config.delegated_qa = [];
|
||||
|
||||
$scope.config.delegated_qa.push($scope.newdqa);
|
||||
$scope.saveSettings();
|
||||
$scope.newdqa = "";
|
||||
}
|
||||
}
|
||||
$scope.dropDelegatedQA = function(member) {
|
||||
if ($scope.config.delegated_qa) {
|
||||
$scope.config.delegated_qa = [];
|
||||
|
||||
angular.forEach($scope.config.delegated_qa, function(m, k) {
|
||||
if (member == m)
|
||||
$scope.config.delegated_qa.splice(k, 1);
|
||||
});
|
||||
$scope.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
$scope.saveChallengeInfo = function() {
|
||||
this.challenge.duration = $scope.duration;
|
||||
@ -2800,11 +2800,9 @@ function presenceCal(scope, location, data) {
|
||||
.attr("width", cellSize)
|
||||
.attr("height", cellSize)
|
||||
.attr("transform", function(d) { return "translate(" + (d.getHours() * cellSize) + "," + (d.getMinutes() / 15 * cellSize) + ")"; })
|
||||
.attr("class", function (d) {
|
||||
if (d >= scope.settings.start && d < scope.settings.start + scope.settings.end - scope.settings.start) return color(data.reduce(function (prev, cur) {
|
||||
.attr("class", function(d) { if (d >= scope.settings.start && d < scope.settings.start + scope.settings.end - scope.settings.start) return color(data.reduce(function(prev, cur){
|
||||
cur = new Date(cur).getTime();
|
||||
dv = d.getTime();
|
||||
return prev + ((dv <= cur && cur < dv+15*60000)?1:0);
|
||||
}, 0));
|
||||
});
|
||||
}, 0)); });
|
||||
}
|
||||
|
@ -306,7 +306,7 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card my-3">
|
||||
<form ng-submit="addDelegatedQA()" class="card my-3">
|
||||
<div class="card-header">
|
||||
<h3>Managers QA</h3>
|
||||
</div>
|
||||
@ -318,12 +318,10 @@
|
||||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form class="row" ng-controller="AllTeamAssociationsController" ng-submit="addDelegatedQA()">
|
||||
<div class="col">
|
||||
<li class="row">
|
||||
<div class="col" ng-controller="AllTeamAssociationsController">
|
||||
<select class="form-control form-control-sm" ng-model="newdqa">
|
||||
<option ng-selected="newdqa == m" ng-repeat="(i,m) in allAssociations" ng-value="m">{{ m }}</option>
|
||||
<option ng-repeat="(i,m) in allAssociations" ng-value="m">{{ m }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col input-group">
|
||||
@ -332,9 +330,10 @@
|
||||
<button class="btn btn-sm btn-success" ng-disabled="!newdqa.length"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form ng-submit="saveChallengeInfo()" class="card my-3">
|
||||
<div class="card-header">
|
||||
|
@ -365,7 +365,7 @@
|
||||
<div class="carousel slide" data-interval="12000" style="padding-bottom: 0px" autocarousel>
|
||||
<div class="carousel-inner">
|
||||
<div class="carousel-item" ng-repeat="theme in themes" ng-class="{active: $first}">
|
||||
<div class="carousel-caption text-indent" ng-if="theme.urlid !== '_'">
|
||||
<div class="carousel-caption text-indent">
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{ theme.image.substr(0, theme.image.length-3) }}thumb.jpg')"></div>
|
||||
<h3 class="text-left" ng-bind="theme.name"></h3>
|
||||
<p class="text-justify" style="font-size: 111%" ng-bind-html="theme.headline"></p>
|
||||
@ -377,8 +377,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card niceborder bg-dark" ng-if="s.type == 'exercice' && !s.params.hide">
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{exercices[s.params.exercice].image}}')" ng-if="exercices[s.params.exercice] && exercices[s.params.exercice].image"></div>
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{themes[my.exercices[s.params.exercice].theme_id].image}}')" ng-if="!exercices[s.params.exercice] || !exercices[s.params.exercice].image"></div>
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{themes[my.exercices[s.params.exercice].theme_id].image}}')"></div>
|
||||
<div class="card-body text-light">
|
||||
<h3 style="font-size: 1.0rem; text-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">Défi <em>{{ exercices[s.params.exercice].title }}</em> du thème <em>{{ themes[my.exercices[s.params.exercice].theme_id].name }}</em></h3>
|
||||
<p ng-bind-html="my.exercices[s.params.exercice].overview"></p>
|
||||
@ -398,8 +397,7 @@
|
||||
Challenges à la une
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{exercices[lastExercice].image}}')" ng-if="exercices[lastExercice] && exercices[lastExercice].image"></div>
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{themes[my.exercices[lastExercice].theme_id].image}}')" ng-if="!exercices[s.params.exercice] || !exercices[s.params.exercice].image"></div>
|
||||
<div class="card-img-top theme-card" style="background-image: url('{{themes[my.exercices[lastExercice].theme_id].image}}')"></div>
|
||||
<div class="card-body text-light">
|
||||
<h3 style="font-size: 1.0rem; text-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap"><em>{{ exercices[lastExercice].title }}</em> du thème <em>{{ themes[my.exercices[lastExercice].theme_id].name }}</em></h3>
|
||||
<p ng-bind-html="my.exercices[lastExercice].overview"></p>
|
||||
|
@ -1,6 +1,5 @@
|
||||
<script>
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import {
|
||||
Badge,
|
||||
@ -63,7 +62,7 @@
|
||||
<NavbarToggler on:click={() => (isOpen = !isOpen)} />
|
||||
<Collapse {isOpen} navbar expand="md" on:update={handleUpdate}>
|
||||
<Nav navbar>
|
||||
<NavItem active={$page.route && $page.route.id === "/"}>
|
||||
<NavItem>
|
||||
<NavLink href=".">
|
||||
<Icon name="house" />
|
||||
Accueil
|
||||
@ -72,7 +71,7 @@
|
||||
<NavThemes />
|
||||
<NavTags />
|
||||
{#if $settings && $settings.end - $settings.start > 0 && $teams && Object.keys($teams).length}
|
||||
<NavItem active={$page.route && $page.route.id === "/rank"}>
|
||||
<NavItem>
|
||||
<NavLink href="rank">
|
||||
<Icon name="sort-down" />
|
||||
Classement
|
||||
@ -80,7 +79,7 @@
|
||||
</NavItem>
|
||||
{/if}
|
||||
<HeaderIssues />
|
||||
<NavItem active={$page.route && $page.route.id === "/rules"}>
|
||||
<NavItem>
|
||||
<NavLink href="rules">
|
||||
<Icon name="signpost-split" />
|
||||
Aide
|
||||
|
@ -1,6 +1,4 @@
|
||||
<script>
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Icon,
|
||||
@ -26,7 +24,7 @@
|
||||
</script>
|
||||
|
||||
{#if $issues.length}
|
||||
<NavItem active={$page.route && $page.route.id === "/issues"}>
|
||||
<NavItem>
|
||||
<NavLink href="issues">
|
||||
<Icon name="bug" />
|
||||
Problèmes
|
||||
|
@ -1,6 +1,4 @@
|
||||
<script>
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Dropdown,
|
||||
@ -16,7 +14,7 @@
|
||||
let filter = "";
|
||||
</script>
|
||||
|
||||
<Dropdown nav inNavbar active={$page.params.tag}>
|
||||
<Dropdown nav inNavbar>
|
||||
<DropdownToggle nav caret>
|
||||
<Icon name="tags" />
|
||||
Tags
|
||||
|
@ -14,7 +14,6 @@
|
||||
import { myThemes, themes } from '$lib/stores/mythemes.js';
|
||||
</script>
|
||||
|
||||
{#if $themes.length > 0 && ($themes[0].id != 0 || $themes.length > 1)}
|
||||
<Dropdown nav inNavbar active={$current_theme && $current_theme.id != 0}>
|
||||
<DropdownToggle nav caret>
|
||||
<Icon name="tv" />
|
||||
@ -55,7 +54,6 @@
|
||||
</div>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
{/if}
|
||||
{#if $themesStore && $themesStore["0"] && $themesStore["0"].exercices}
|
||||
<Dropdown nav inNavbar active={$current_theme && $current_theme && $current_theme.id == 0}>
|
||||
<DropdownToggle nav caret>
|
||||
|
@ -73,9 +73,7 @@
|
||||
</Column>
|
||||
</Table>
|
||||
{:else}
|
||||
<CardBody>
|
||||
Vous n'avez fait aucune action vous faisant gagner ou perdre des points.
|
||||
</CardBody>
|
||||
{/if}
|
||||
<button class="btn btn-primary" on:click={refresh_scores}>
|
||||
<Icon name="arrow-clockwise" />
|
||||
|
@ -45,7 +45,6 @@
|
||||
|
||||
{#if exercices.length}
|
||||
<Row cols="3">
|
||||
{#key exercices}
|
||||
{#each exercices as {theme, exercice, index} (index)}
|
||||
<Col class="mb-3">
|
||||
<CardTheme
|
||||
@ -55,7 +54,6 @@
|
||||
/>
|
||||
</Col>
|
||||
{/each}
|
||||
{/key}
|
||||
</Row>
|
||||
{:else}
|
||||
<p class="lead">
|
||||
|
@ -56,26 +56,6 @@ func GetThemes() ([]*Theme, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetThemesExtended returns a list of Themes including standalone exercices.
|
||||
func GetThemesExtended() ([]*Theme, error) {
|
||||
if themes, err := GetThemes(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// Append standalone exercices fake-themes
|
||||
stdthm := &Theme{
|
||||
Name: "Défis indépendants",
|
||||
URLId: "_",
|
||||
Path: "exercices",
|
||||
}
|
||||
|
||||
if exercices, err := stdthm.GetExercices(); err == nil && len(exercices) > 0 {
|
||||
themes = append(themes, stdthm)
|
||||
}
|
||||
|
||||
return themes, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetTheme retrieves a Theme from its identifier.
|
||||
func GetTheme(id int64) (*Theme, error) {
|
||||
t := &Theme{}
|
||||
|
@ -38,10 +38,6 @@ func init() {
|
||||
oidcRedirectURL = v
|
||||
}
|
||||
|
||||
if v, ok := os.LookupEnv("FIC_GITLAB_BASEURL"); ok {
|
||||
gitlabBaseURL = v
|
||||
}
|
||||
|
||||
flag.StringVar(&oidcRedirectURL, "oidc-redirect", oidcRedirectURL, "Base URL for the redirect after connection")
|
||||
flag.StringVar(&gitlabBaseURL, "gitlab-baseurl", gitlabBaseURL, "Base URL of the Gitlab instance")
|
||||
flag.StringVar(&gitlabClientID, "gitlab-clientid", os.Getenv("FIC_GITLAB_CLIENT_ID"), "ClientID for GitLab's OIDC")
|
||||
|
@ -118,7 +118,7 @@ func getExerciceQA(c *gin.Context) {
|
||||
func exportQA(c *gin.Context) {
|
||||
var report string
|
||||
|
||||
themes, err := fic.GetThemesExtended()
|
||||
themes, err := fic.GetThemes()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetThemes: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list themes: %s", err.Error())})
|
||||
@ -191,7 +191,7 @@ type ExportReport struct {
|
||||
func exportQAJSON(c *gin.Context) {
|
||||
report := map[string]ExportTheme{}
|
||||
|
||||
themes, err := fic.GetThemesExtended()
|
||||
themes, err := fic.GetThemes()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetThemes: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list themes: %s", err.Error())})
|
||||
|
@ -1,11 +1,8 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
@ -20,7 +17,6 @@ func declareTodoRoutes(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
func declareTodoManagerRoutes(router *gin.RouterGroup) {
|
||||
router.POST("/qa_assign_work", assignWork)
|
||||
router.POST("/qa_my_exercices.json", addQAView)
|
||||
router.POST("/qa_work.json", createQATodo)
|
||||
|
||||
@ -239,80 +235,3 @@ func deleteQATodo(c *gin.Context) {
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
type QAAssignWork struct {
|
||||
Turns int `json:"turns"`
|
||||
Start int `json:"start"`
|
||||
TeamPrefix string `json:"team_prefix"`
|
||||
TeamAssistants string `json:"team_assistants"`
|
||||
}
|
||||
|
||||
func assignWork(c *gin.Context) {
|
||||
var uaw QAAssignWork
|
||||
if err := c.ShouldBindJSON(&uaw); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
if uaw.Turns == 0 {
|
||||
uaw.Turns = 1
|
||||
}
|
||||
if uaw.TeamPrefix == "" {
|
||||
uaw.TeamPrefix = "FIC Groupe "
|
||||
}
|
||||
if uaw.TeamAssistants == "" {
|
||||
uaw.TeamAssistants = "assistants"
|
||||
}
|
||||
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list teams: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
// Remove assistant team
|
||||
for tid := len(teams) - 1; tid >= 0; tid-- {
|
||||
team := teams[tid]
|
||||
if strings.Contains(strings.ToLower(team.Name), uaw.TeamAssistants) {
|
||||
teams = append(teams[:tid], teams[tid+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
exercices, err := fic.GetExercices()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetExercices: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list exercices: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
// Struct to store reported team (due to owned exercice)
|
||||
var teamIdStack []int64
|
||||
|
||||
for i := 0; i < uaw.Turns; i++ {
|
||||
for eid, ex := range exercices {
|
||||
team := teams[(uaw.Start+eid+uaw.Turns*i)%len(teams)]
|
||||
|
||||
if len(teamIdStack) > 0 {
|
||||
teamIdStack = append(teamIdStack, team.Id)
|
||||
team, _ = fic.GetTeam(teamIdStack[0])
|
||||
teamIdStack = append([]int64{}, teamIdStack[1:]...)
|
||||
}
|
||||
|
||||
j := 0
|
||||
// Find a team not responsible for this exercice
|
||||
for (strings.Contains(ex.Path, "grp") && strings.Contains(ex.Path, "-grp"+strings.TrimPrefix(team.Name, uaw.TeamPrefix)+"-")) || (!strings.Contains(ex.Path, "grp") && strings.HasPrefix(ex.Path, strings.TrimPrefix(team.Name, uaw.TeamPrefix)+"-")) {
|
||||
j += 1
|
||||
teamIdStack = append(teamIdStack, team.Id)
|
||||
team = teams[(uaw.Start+eid+uaw.Turns*i+j)%len(teams)]
|
||||
}
|
||||
|
||||
_, err := team.NewQATodo(ex.Id)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, "true")
|
||||
}
|
||||
|
@ -15,11 +15,6 @@ import (
|
||||
|
||||
func reloadSettings(config *settings.Settings) {
|
||||
api.ManagerUsers = config.DelegatedQA
|
||||
fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth
|
||||
fic.UnlockedChallengeUpTo = config.UnlockedChallengeUpTo
|
||||
fic.UnlockedStandaloneExercices = config.UnlockedStandaloneExercices
|
||||
fic.UnlockedStandaloneExercicesByThemeStepValidation = config.UnlockedStandaloneExercicesByThemeStepValidation
|
||||
fic.UnlockedStandaloneExercicesByStandaloneExerciceValidation = config.UnlockedStandaloneExercicesByStandaloneExerciceValidation
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -23,7 +23,6 @@
|
||||
} from '@sveltestrap/sveltestrap';
|
||||
|
||||
import { auth, gitlab, version } from '$lib/stores/auth';
|
||||
import { themes } from '$lib/stores/themes';
|
||||
|
||||
export let activemenu = "";
|
||||
$: {
|
||||
@ -51,7 +50,6 @@
|
||||
<span class="d-none d-md-inline">Accueil</span>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
{#if $themes.length}
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="themes"
|
||||
@ -61,7 +59,6 @@
|
||||
<span class="d-none d-md-inline">Scénarios</span>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
{/if}
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="exercices"
|
||||
@ -81,6 +78,15 @@
|
||||
<span class="d-none d-md-inline">Équipes</span>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="repositories"
|
||||
active={activemenu === 'repositories'}
|
||||
>
|
||||
<Icon name="archive" />
|
||||
<span class="d-none d-md-inline">Dépôts</span>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
{/if}
|
||||
</Nav>
|
||||
<Nav class="ms-auto text-light" navbar>
|
||||
|
@ -83,8 +83,6 @@
|
||||
<td>
|
||||
{#if $exercicesIdx.length == 0 && $themesIdx.length == 0}
|
||||
<Spinner size="sm" />
|
||||
{:else if !$themesIdx[$exercicesIdx[todo.id_exercice].id_theme]}
|
||||
Défis indépendants
|
||||
{:else}
|
||||
<a href="themes/{$exercicesIdx[todo.id_exercice].id_theme}">
|
||||
{$themesIdx[$exercicesIdx[todo.id_exercice].id_theme].name}
|
||||
|
@ -29,7 +29,6 @@
|
||||
style="overflow-y: auto"
|
||||
>
|
||||
{#await themesP then themes}
|
||||
{#if Object.keys(themes).length > 1}
|
||||
<Row
|
||||
style={'min-width:'+15*Object.keys(themes).length + 'vw'}
|
||||
>
|
||||
@ -70,48 +69,5 @@
|
||||
</Col>
|
||||
{/each}
|
||||
</Row>
|
||||
{:else}
|
||||
{#each Object.keys(themes) as tname}
|
||||
{#if themes[tname].exercices}
|
||||
{#each themes[tname].exercices as exercice}
|
||||
<Row
|
||||
style={'min-width:'+15*Object.keys(themes).length + 'vw'}
|
||||
>
|
||||
<Col style="border-right: 1px solid lightgray">
|
||||
<h3
|
||||
class="text-center py-3 mb-3"
|
||||
style="border-bottom: 2px solid black"
|
||||
on:click={() => goto(`exercices/${exercice.exercice.id}`)}
|
||||
>
|
||||
{exercice.exercice.title}
|
||||
</h3>
|
||||
{#if exercice.reports}
|
||||
{#each exercice.reports as report}
|
||||
<Card
|
||||
class="mb-3"
|
||||
color={state2Color(report.report.state)}
|
||||
style="cursor: pointer"
|
||||
on:click={() => goto(`exercices/${exercice.exercice.id}/${report.report.id}`)}
|
||||
>
|
||||
<CardBody class="p-2">
|
||||
{report.report.subject}
|
||||
{#if report.comments}
|
||||
<Badge
|
||||
class="float-end"
|
||||
>
|
||||
{report.comments.length}
|
||||
</Badge>
|
||||
{/if}
|
||||
</CardBody>
|
||||
</Card>
|
||||
{/each}
|
||||
<hr />
|
||||
{/if}
|
||||
</Col>
|
||||
</Row>
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
|
@ -44,7 +44,6 @@ export const exercicesByTheme = derived(
|
||||
const exercices_idx = { };
|
||||
|
||||
for (const e of $exercices) {
|
||||
if (!e.id_theme) e.id_theme = 0;
|
||||
if (!exercices_idx[e.id_theme]) {
|
||||
exercices_idx[e.id_theme] = []
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ export function createTodosStore(team) {
|
||||
|
||||
refresh: async () => {
|
||||
const list = await getQATodo(team);
|
||||
list.map((e) => e.id += 10000000);
|
||||
list.push(...await getQAWork(team));
|
||||
update((m) => list);
|
||||
return list;
|
||||
|
@ -22,14 +22,11 @@ export class Team {
|
||||
toHexColor() {
|
||||
let num = this.color;
|
||||
num >>>= 0;
|
||||
let b = (num & 0xFF).toString(16),
|
||||
g = ((num & 0xFF00) >>> 8).toString(16),
|
||||
r = ((num & 0xFF0000) >>> 16).toString(16),
|
||||
let b = num & 0xFF,
|
||||
g = (num & 0xFF00) >>> 8,
|
||||
r = (num & 0xFF0000) >>> 16,
|
||||
a = ( (num & 0xFF000000) >>> 24 ) / 255 ;
|
||||
if (r.length <= 1) r = "0" + r;
|
||||
if (g.length <= 1) g = "0" + g;
|
||||
if (b.length <= 1) b = "0" + b;
|
||||
return "#" + r + g + b;
|
||||
return "#" + r.toString(16) + g.toString(16) + b.toString(16);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,12 +23,7 @@ export class Theme {
|
||||
export async function getThemes() {
|
||||
const res = await fetch(`api/themes`, {headers: {'Accept': 'application/json'}})
|
||||
if (res.status == 200) {
|
||||
const data = await res.json();
|
||||
if (data) {
|
||||
return data.map((t) => new Theme(t));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
return (await res.json()).map((t) => new Theme(t));
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@
|
||||
<tbody>
|
||||
{#each exercices as exercice (exercice.id)}
|
||||
{#if exercice.title.indexOf(query) >= 0}
|
||||
<tr on:click={() => show(exercice.id)} style="cursor: pointer">
|
||||
<tr on:click={() => show(exercice.id)}>
|
||||
{#each fieldsExercices as field}
|
||||
<td>
|
||||
{@html exercice[field]}
|
||||
|
@ -4,12 +4,7 @@
|
||||
import { teams } from '$lib/stores/teams';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Container,
|
||||
FormGroup,
|
||||
Input,
|
||||
Label,
|
||||
Spinner,
|
||||
Table,
|
||||
} from '@sveltestrap/sveltestrap';
|
||||
|
||||
@ -21,44 +16,6 @@
|
||||
function show(id) {
|
||||
goto("teams/" + id)
|
||||
}
|
||||
|
||||
let start = 0;
|
||||
let turns = 3;
|
||||
let team_prefix = "";
|
||||
let team_assistants = "";
|
||||
let assignInProgress = false;
|
||||
|
||||
async function assignExercices() {
|
||||
assignInProgress = true;
|
||||
const res = await fetch(`api/qa_assign_work`, {
|
||||
method: 'POST',
|
||||
headers: {'Accept': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
start,
|
||||
turns,
|
||||
team_prefix,
|
||||
team_assistants,
|
||||
}),
|
||||
})
|
||||
if (res.status == 200) {
|
||||
teams.refresh();
|
||||
assignInProgress = false;
|
||||
} else {
|
||||
assignInProgress = false;
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteAssignation() {
|
||||
const res = await fetch(`api/qa_assign_work`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
if (res.status == 200) {
|
||||
teams.refresh();
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container class="mt-2 mb-5">
|
||||
@ -82,19 +39,10 @@
|
||||
<tbody>
|
||||
{#each $teams as team (team.id)}
|
||||
{#if team.name.indexOf(query) >= 0}
|
||||
<tr on:click={() => show(team.id)} style="cursor: pointer">
|
||||
<tr on:click={() => show(team.id)}>
|
||||
{#each fields as field}
|
||||
<td class:text-end={field == "image"}>
|
||||
{#if field == "color"}
|
||||
<div
|
||||
class="badge"
|
||||
style={"background-color: " + team.toHexColor()}
|
||||
>
|
||||
{team.toHexColor()}
|
||||
</div>
|
||||
{:else}
|
||||
{team[field]}
|
||||
{/if}
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
@ -102,48 +50,4 @@
|
||||
{/each}
|
||||
</tbody>
|
||||
</Table>
|
||||
|
||||
<hr>
|
||||
<h2>
|
||||
Assigner des exercices aux équipes
|
||||
</h2>
|
||||
|
||||
<form on:submit|preventDefault={assignExercices}>
|
||||
<FormGroup>
|
||||
<Label for="ae-start">Compteur de départ</Label>
|
||||
<Input type="number" id="ae-start" bind:value={start} />
|
||||
<p class="form-text">
|
||||
Incrémenter de 1 pour chaque nouveau challenge blanc, cela décale l'attribution des exercices.
|
||||
</p>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="ae-turns">Nombre d'itérations</Label>
|
||||
<Input type="number" id="ae-turns" bind:value={turns} />
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="ae-prefix">Préfixe des noms d'équipes</Label>
|
||||
<Input id="ae-prefix" bind:value={team_prefix} placeholder="FIC Groupe" />
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="ae-assistants">Nom de l'équipe assistants</Label>
|
||||
<Input id="ae-assistants" bind:value={team_assistants} placeholder="Assistants" />
|
||||
</FormGroup>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={assignInProgress}
|
||||
color="primary"
|
||||
>
|
||||
{#if assignInProgress}
|
||||
<Spinner size="sm" />
|
||||
{/if}
|
||||
Assigner des exercices
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="danger"
|
||||
on:click={deleteAssignation}
|
||||
>
|
||||
Supprimer toute assignation
|
||||
</Button>
|
||||
</form>
|
||||
</Container>
|
||||
|
@ -102,8 +102,6 @@
|
||||
<td>
|
||||
{#if $exercicesIdx.length == 0 && $themesIdx.length == 0}
|
||||
<Spinner size="sm" />
|
||||
{:else if !$themesIdx[$exercicesIdx[todo.id_exercice].id_theme]}
|
||||
Défis indépendants
|
||||
{:else}
|
||||
<a href="themes/{$exercicesIdx[todo.id_exercice].id_theme}">
|
||||
{$themesIdx[$exercicesIdx[todo.id_exercice].id_theme].name}
|
||||
@ -143,7 +141,7 @@
|
||||
bind:value={newTodo}
|
||||
>
|
||||
{#each Object.keys($exercicesByTheme) as thid}
|
||||
<optgroup label={(thid != "0" ? $themesIdx[thid].name : "Exercices indépendants")}>
|
||||
<optgroup label={$themesIdx[thid].name}>
|
||||
{#each $exercicesByTheme[thid] as exercice (exercice.id)}
|
||||
<option value={exercice.id}>{exercice.title}</option>
|
||||
{/each}
|
||||
@ -173,9 +171,7 @@
|
||||
bind:value={newThemeTodo}
|
||||
>
|
||||
{#each Object.keys($exercicesByTheme) as thid}
|
||||
{#if thid != "0"}
|
||||
<option value={$themesIdx[thid].id}>{$themesIdx[thid].name}</option>
|
||||
{/if}
|
||||
{/each}
|
||||
</select>
|
||||
<Button
|
||||
|
@ -60,7 +60,7 @@
|
||||
<tbody>
|
||||
{#each $themes as theme (theme.id)}
|
||||
{#if theme.name.indexOf(query) >= 0 || theme.authors.indexOf(query) >= 0 || theme.intro.indexOf(query) >= 0}
|
||||
<tr on:click={() => show(theme.id)} style="cursor: pointer">
|
||||
<tr on:click={() => show(theme.id)}>
|
||||
{#each fields as field}
|
||||
<td class:text-end={field == "image"}>
|
||||
{#if field == "image"}
|
||||
|
@ -63,7 +63,7 @@
|
||||
<tbody>
|
||||
{#each exercices as exercice (exercice.id)}
|
||||
{#if exercice.title.indexOf(query) >= 0}
|
||||
<tr on:click={() => show(exercice.id)} style="cursor: pointer">
|
||||
<tr on:click={() => show(exercice.id)}>
|
||||
{#each fieldsExercices as field}
|
||||
<td>
|
||||
{@html exercice[field]}
|
||||
|
Loading…
x
Reference in New Issue
Block a user