Add TODO for Problems page
[vchess.git] / client / src / views / Problems.vue
1 <template lang="pug">
2 main
3 input#modalNewprob.modal(type="checkbox" @change="infoMsg=''")
4 div#newprobDiv(role="dialog" data-checkbox="modalNewprob")
5 .card(@keyup.enter="sendProblem()")
6 label#closeNewprob.modal-close(for="modalNewprob")
7 fieldset
8 label(for="selectVariant") {{ st.tr["Variant"] }}
9 select#selectVariant(
10 v-model="curproblem.vid"
11 @change="changeVariant(curproblem)"
12 )
13 option(
14 v-for="v in [emptyVar].concat(st.variants)"
15 :value="v.id"
16 :selected="curproblem.vid==v.id"
17 )
18 | {{ v.name }}
19 fieldset
20 label(for="inputFen") FEN
21 input#inputFen(
22 type="text"
23 v-model="curproblem.fen"
24 @input="trySetDiagram(curproblem)"
25 )
26 div(v-html="curproblem.diag")
27 fieldset
28 textarea#instructions(
29 :placeholder="st.tr['Instructions']"
30 v-model="curproblem.instruction"
31 )
32 p(v-html="parseHtml(curproblem.instruction)")
33 fieldset
34 textarea#solution(
35 :placeholder="st.tr['Solution']"
36 v-model="curproblem.solution"
37 )
38 p(v-html="parseHtml(curproblem.solution)")
39 button(@click="sendProblem()") {{ st.tr["Send"] }}
40 #dialog.text-center {{ st.tr[infoMsg] }}
41 .row
42 .col-sm-12
43 button#newProblem(onClick="doClick('modalNewprob')")
44 | {{ st.tr["New problem"] }}
45 .row(v-if="showOne")
46 .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
47 #actions
48 button(@click="showOne=false") {{ st.tr["Back to list"] }}
49 button(
50 v-if="st.user.id == curproblem.uid"
51 @click="editProblem(curproblem)"
52 )
53 | {{ st.tr["Edit"] }}
54 button(
55 v-if="st.user.id == curproblem.uid"
56 @click="deleteProblem(curproblem)"
57 )
58 | {{ st.tr["Delete"] }}
59 h4 {{ curproblem.vname }}
60 p(v-html="parseHtml(curproblem.instruction)")
61 h4(@click="curproblem.showSolution=!curproblem.showSolution")
62 | {{ st.tr["Show solution"] }}
63 p(
64 v-show="curproblem.showSolution"
65 v-html="parseHtml(curproblem.solution)"
66 )
67 .row(v-else)
68 .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
69 label(for="checkboxMine") {{ st.tr["My problems"] }}
70 input#checkboxMine(
71 type="checkbox"
72 v-model="onlyMines"
73 )
74 label(for="selectVariant") {{ st.tr["Variant"] }}
75 select#selectVariant(v-model="selectedVar")
76 option(
77 v-for="v in [emptyVar].concat(st.variants)"
78 :value="v.id"
79 )
80 | {{ v.name }}
81 div(
82 v-for="p in problems"
83 v-show="displayProblem(p)"
84 @click="showProblem(p)"
85 )
86 h4 {{ p.vname }}
87 p {{ p.fen }}
88 p(v-html="p.instruction")
89 BaseGame(v-if="showOne" :game="game" :vr="vr")
90 </template>
91
92 <script>
93 // TODO: si showProblem(p), changer URL (ajouter problem ID)
94 // Et si au lancement l'URL comprend un pid, alors showOne=true et curproblem=...
95 // TODO: also style problem div (in the list, similar to variants page + clickable)
96
97 import { store } from "@/store";
98 import { ajax } from "@/utils/ajax";
99 import { checkProblem } from "@/data/problemCheck";
100 import { getDiagram } from "@/utils/printDiagram";
101 import BaseGame from "@/components/BaseGame.vue";
102 export default {
103 name: "my-problems",
104 components: {
105 BaseGame,
106 },
107 data: function() {
108 return {
109 st: store.state,
110 emptyVar: {
111 vid: 0,
112 vname: "",
113 },
114 // Problem currently showed, or edited:
115 curproblem: {
116 id: 0, //used in case of edit
117 vid: 0,
118 fen: "",
119 diag: "",
120 instruction: "",
121 solution: "",
122 showSolution: false,
123 },
124 loadedVar: 0, //corresponding to loaded V
125 selectedVar: 0, //to filter problems based on variant
126 problems: [],
127 onlyMines: false,
128 showOne: false,
129 infoMsg: "",
130 vr: null, //"variant rules" object initialized from FEN
131 game: {
132 players:[{name:"Problem"},{name:"Problem"}],
133 mode: "analyze",
134 },
135 };
136 },
137 created: function() {
138 ajax("/problems", "GET", (res) => {
139 this.problems = res.problems;
140 if (this.st.variants.length > 0)
141 this.problems.forEach(p => this.setVname(p))
142 });
143 },
144 watch: {
145 // st.variants changes only once, at loading from [] to [...]
146 "st.variants": function(variantArray) {
147 // Set problems vname (either all are set or none)
148 if (this.problems.length > 0 && this.problems[0].vname == "")
149 this.problems.forEach(p => this.setVname(p));
150 },
151 },
152 methods: {
153 setVname: function(prob) {
154 prob.vname = this.st.variants.find(v => v.id == prob.vid).name;
155 },
156 copyProblem: function(p1, p2) {
157 for (let key in p1)
158 p2[key] = p1[key];
159 },
160 resetCurProb: function() {
161 this.curproblem.id = 0;
162 this.curproblem.uid = 0;
163 this.curproblem.vid = "";
164 this.curproblem.vname = "";
165 this.curproblem.fen = "";
166 this.curproblem.diag = "";
167 this.curproblem.instruction = "";
168 this.curproblem.solution = "";
169 this.curproblem.showSolution = false;
170 },
171 parseHtml: function(txt) {
172 return !txt.match(/<[/a-zA-Z]+>/)
173 ? txt.replace(/\n/g, "<br/>") //no HTML tag
174 : txt;
175 },
176 changeVariant: function(prob) {
177 this.setVname(prob);
178 this.loadVariant(
179 prob.vid,
180 () => {
181 // Set FEN if possible (might not be correct yet)
182 if (V.IsGoodFen(prob.fen))
183 this.setDiagram(prob);
184 }
185 );
186 },
187 loadVariant: async function(vid, cb) {
188 // Condition: vid is a valid variant ID
189 this.loadedVar = 0;
190 const variant = this.st.variants.find(v => v.id == vid);
191 const vModule = await import("@/variants/" + variant.name + ".js");
192 window.V = vModule.VariantRules;
193 this.loadedVar = vid;
194 cb();
195 },
196 trySetDiagram: function(prob) {
197 // Problem edit: FEN could be wrong or incomplete,
198 // variant could not be ready, or not defined
199 if (prob.vid > 0 && this.loadedVar == prob.vid && V.IsGoodFen(prob.fen))
200 this.setDiagram(prob);
201 },
202 setDiagram: function(prob) {
203 // Condition: prob.fen is correct and global V is ready
204 const parsedFen = V.ParseFen(prob.fen);
205 const args = {
206 position: parsedFen.position,
207 orientation: parsedFen.turn,
208 };
209 prob.diag = getDiagram(args);
210 },
211 displayProblem: function(p) {
212 return ((this.selectedVar == 0 || p.vid == this.selectedVar) &&
213 ((this.onlyMines && p.uid == this.st.user.id)
214 || (!this.onlyMines && p.uid != this.st.user.id)));
215 },
216 showProblem: function(p) {
217 this.loadVariant(
218 p.vid,
219 () => {
220 // The FEN is already checked at this stage:
221 this.vr = new V(p.fen);
222 this.game.vname = p.vname;
223 this.game.mycolor = this.vr.turn; //diagram orientation
224 this.game.fen = p.fen;
225 this.$set(this.game, "fenStart", p.fen);
226 this.copyProblem(p, this.curproblem);
227 this.showOne = true;
228 }
229 );
230 },
231 sendProblem: function() {
232 const error = checkProblem(this.curproblem);
233 if (!!error)
234 return alert(error);
235 const edit = this.curproblem.id > 0;
236 this.infoMsg = "Processing... Please wait";
237 ajax(
238 "/problems",
239 edit ? "PUT" : "POST",
240 {prob: this.curproblem},
241 (ret) => {
242 if (edit)
243 {
244 let editedP = this.problems.find(p => p.id == this.curproblem.id);
245 this.copyProblem(this.curproblem, editedP);
246 }
247 else //new problem
248 {
249 let newProblem = Object.assign({}, this.curproblem);
250 newProblem.id = ret.id;
251 this.problems = this.problems.concat(newProblem);
252 }
253 this.resetCurProb();
254 this.infoMsg = "";
255 }
256 );
257 },
258 editProblem: function(prob) {
259 if (!prob.diag)
260 this.setDiagram(prob); //possible because V is loaded at this stage
261 this.copyProblem(prob, this.curproblem);
262 doClick('modalNewprob');
263 },
264 deleteProblem: function(prob) {
265 if (confirm(this.st.tr["Are you sure?"]))
266 ajax("/problems", "DELETE", {pid:prob.id});
267 },
268 },
269 };
270 </script>
271
272 <style lang="sass" scoped>
273 #newProblem
274 display: block
275 margin: 10px auto 5px auto
276 </style>