commit
a479cb8abc
@ -0,0 +1,111 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<!-- Required meta tags --> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||||
|
|
||||||
|
<!-- Bootstrap CSS --> |
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> |
||||||
|
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet"> |
||||||
|
|
||||||
|
<script src="setup/upload/server/php/files/usersdb.js"></script> |
||||||
|
<script src="https://aframe.io/releases/1.0.0/aframe.min.js"></script> |
||||||
|
</head> |
||||||
|
<style> |
||||||
|
body { background-color: transparent; } |
||||||
|
|
||||||
|
#email, #sms { |
||||||
|
font-size: xx-large; |
||||||
|
} |
||||||
|
</style> |
||||||
|
|
||||||
|
<body> |
||||||
|
|
||||||
|
<img src="setup/productlogo.png"> |
||||||
|
|
||||||
|
<h3>flat viewer by category sorted by position</h3> |
||||||
|
|
||||||
|
<div id="spacesholder">Your spaces: |
||||||
|
<ul id="spaces"></ul> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div>Already have an account? <span onclick="login()" style="text-decoration: underline;">Log-in</span> |
||||||
|
<form> |
||||||
|
<div id="login" style="display:none"><input id="useremail"/> |
||||||
|
<button style="margin-top:2px;" type="button" onclick="loginViaEmail()" id="loginemail" class="btn btn-lg btn-primary btn-block">Login</button> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
<div style="display:none" id="nouser">User not found. Double check your email address then contact fabien@iterative-explorations.com</a></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<script> |
||||||
|
const urlParams = new URLSearchParams(window.location.search); |
||||||
|
const email = urlParams.get('email'); |
||||||
|
if (email) loginViaEmail() |
||||||
|
|
||||||
|
var uploadedURL = "setup/upload/server/php/files/"; |
||||||
|
var images = [] |
||||||
|
var url = "/" |
||||||
|
var urlParameters = "?customimages=" |
||||||
|
var userspace |
||||||
|
|
||||||
|
function login(){ |
||||||
|
document.querySelector("#login").style.display = "block" |
||||||
|
} |
||||||
|
|
||||||
|
function findCategory(image){ |
||||||
|
if (!userspace) return |
||||||
|
console.log(image.filename) |
||||||
|
var imagePos = new THREE.Vector3(); |
||||||
|
imagePos.copy ( AFRAME.utils.coordinates.parse(image.position) ) |
||||||
|
|
||||||
|
var closest |
||||||
|
var smallestDistance = 1000 |
||||||
|
for (var category of userspace.categories){ |
||||||
|
var categoryPos = new THREE.Vector3(); |
||||||
|
categoryPos.copy ( AFRAME.utils.coordinates.parse(category.position) ) |
||||||
|
var distance = categoryPos.distanceTo( imagePos ) |
||||||
|
if (distance < smallestDistance){ |
||||||
|
smallestDistance = distance |
||||||
|
closest = category.label |
||||||
|
} |
||||||
|
console.log(distance, category.label) |
||||||
|
} |
||||||
|
|
||||||
|
return closest |
||||||
|
} |
||||||
|
|
||||||
|
function loginViaEmail(){ |
||||||
|
var path = "setup/" |
||||||
|
if (!email) |
||||||
|
email = document.querySelector("#useremail").value |
||||||
|
userspace = database[email] |
||||||
|
if (!userspace){ |
||||||
|
document.querySelector("#nouser").style.display = "block" |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
document.querySelector("#spacesholder").style.display = "block" |
||||||
|
var spaces = document.querySelector("#spaces") |
||||||
|
var space = document.createElement("li") |
||||||
|
var spacelink = document.createElement("a") |
||||||
|
var images = userspace.files |
||||||
|
spacelink.href = url + urlParameters |
||||||
|
spacelink.target = "_blank" |
||||||
|
spacelink.innerHTML = userspace.last_login |
||||||
|
space.appendChild(spacelink) |
||||||
|
for (var image of images){ |
||||||
|
urlParameters += image.filename + "," |
||||||
|
space.innerHTML += path + image.filename + " " + findCategory( image ) |
||||||
|
} |
||||||
|
console.log(space) |
||||||
|
spaces.appendChild(space) |
||||||
|
console.log("userspace", userspace) |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<h3 style="position:absolute; bottom:0px; right:0px;">A product by <a href="https://iterative-explorations.com"><img width="200px" src="https://iterative-explorations.com/logo.svg"></a>.</h3> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,150 @@ |
|||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>Think + Relax space by Iterative Explorations</title> |
||||||
|
<script src="https://aframe.io/releases/1.0.0/aframe.min.js"></script> |
||||||
|
<script src="https://rawgit.com/feiss/aframe-environment-component/master/dist/aframe-environment-component.min.js"></script> |
||||||
|
<script src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v4.1.2/dist/aframe-extras.min.js"></script> |
||||||
|
<script src="https://unpkg.com/super-hands@3.0.0/dist/super-hands.min.js"></script> |
||||||
|
<script src="pimvrhelpers.js"></script> |
||||||
|
</head> |
||||||
|
<script> |
||||||
|
// could add an in VR edit mode for the categories, to position them, name them, etc |
||||||
|
// should be mostly for adjustments |
||||||
|
var timer = AFRAME.utils.getUrlParameter('timer') |
||||||
|
if (!timer) timer = 300 //5min |
||||||
|
|
||||||
|
function saveNewItemsPosition(){ |
||||||
|
var selector = ".notes"; |
||||||
|
var pos = "" |
||||||
|
document.querySelectorAll(selector).forEach( |
||||||
|
e => pos += "\n" + AFRAME.utils.coordinates.stringify( e.getAttribute("position") ) |
||||||
|
) |
||||||
|
pimvrSaveRemote("WeWorkTest", pos) // from pimvrhelpers.js |
||||||
|
// should be saved by username by space |
||||||
|
// could be pointed at from the database file (thus having multiple storages) |
||||||
|
} |
||||||
|
|
||||||
|
AFRAME.registerComponent("watch", { |
||||||
|
init: function() { |
||||||
|
document.querySelector("#timer").setAttribute("text","value:"+timer) |
||||||
|
this.tick = AFRAME.utils.throttleTick(this.tick, 1000, this); |
||||||
|
// details https://aframe.io/docs/1.0.0/core/utils.html#aframe-utils-throttle-function-minimuminterval-optionalcontext |
||||||
|
}, |
||||||
|
|
||||||
|
tick: function (t, dt) { |
||||||
|
var time = Number( document.querySelector("#timer").getAttribute("text").value ) |
||||||
|
time-- |
||||||
|
document.querySelector("#timer").setAttribute("text","value:"+(time)) |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
|
AFRAME.registerComponent("relaxing-introduction", { |
||||||
|
init: function() { |
||||||
|
// get in flow state |
||||||
|
// for now controlled just with AFrame animation |
||||||
|
// cf https://aframe.io/docs/1.0.0/components/animation.html |
||||||
|
|
||||||
|
setTimeout(function(){ |
||||||
|
for (var note of document.querySelectorAll(".notes")){ note.emit("fadein") } |
||||||
|
}, 10 * 1000) // arbitrary 10s, should be instead after the introduction is finished |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
AFRAME.registerComponent("decompression-conclusion", { |
||||||
|
init: function() { |
||||||
|
// cf PoC at 2017 W3C web authoring workshop with Roland Dubois |
||||||
|
setTimeout(function(){ |
||||||
|
var intructions = document.querySelector("#instructions") |
||||||
|
instructions.setAttribute("position", "-1 1.5 -1") // assumes the user is roughtly centered... could force on camera instead |
||||||
|
instructions.setAttribute("text", "value", "Your session will end soon\n\nVisit https://learnWebVR.xyz/flat.html\n\nto work outside of VR.") |
||||||
|
instructions.setAttribute("opacity", "1") // could be animated instead |
||||||
|
|
||||||
|
console.log("X min voice over and notes fade-out with contextual information (time, weather, etc)") |
||||||
|
|
||||||
|
document.querySelector("[environment]").setAttribute("environment", "preset:checkerboard") |
||||||
|
}, timer * 1000); |
||||||
|
|
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
AFRAME.registerComponent("environment-by-url", { |
||||||
|
schema: {}, |
||||||
|
init: function() { |
||||||
|
var environment = AFRAME.utils.getUrlParameter('environment') |
||||||
|
if (environment) document.querySelector("[environment]").setAttribute("environment", "preset:" + environment) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
AFRAME.registerComponent("photo-importer", { |
||||||
|
schema: {}, |
||||||
|
init: function() { |
||||||
|
var baseURL = "setup/" |
||||||
|
var images = AFRAME.utils.getUrlParameter('customimages').split(','); |
||||||
|
if (images.length == 1) window.location = baseURL; // no images? must be configured first |
||||||
|
// could also display a tutorial instead |
||||||
|
var i=1 |
||||||
|
for (var image of images){ |
||||||
|
if (image){ |
||||||
|
var id = "placeholder"+i |
||||||
|
var placeholder = document.querySelector("#"+id) |
||||||
|
if (!placeholder){ |
||||||
|
// TODO somehow not interactable anymore?! |
||||||
|
// had a similar silly issue before but can't recall how I fixed it :( |
||||||
|
// something basic about entities or hierarchy... |
||||||
|
placeholder = document.createElement("a-box") |
||||||
|
placeholder.id = id |
||||||
|
placeholder.setAttribute("material", "shader:flat") |
||||||
|
placeholder.setAttribute("hoverable", "") |
||||||
|
placeholder.setAttribute("grabbable", "") |
||||||
|
placeholder.setAttribute("draggable", "") |
||||||
|
placeholder.setAttribute("dropppable", "") |
||||||
|
// somehow does appear visually correct but are NOT interactable! |
||||||
|
placeholder.setAttribute("position", "" + i/1.5-1.5 + " 1.5 -0.7") |
||||||
|
placeholder.setAttribute("scale", "0.5 0.3 0.01") |
||||||
|
// assume 1 * 1.5 photo ratio (landscape) |
||||||
|
AFRAME.scenes[0].appendChild(placeholder) |
||||||
|
} |
||||||
|
placeholder.className += "notes" |
||||||
|
placeholder.setAttribute("opacity", "0") |
||||||
|
placeholder.setAttribute("animation", "property: components.material.material.opacity; to: 1; dur: 1500; easing: linear; startEvents: fadein;") |
||||||
|
placeholder.setAttribute("src", baseURL+image) |
||||||
|
i++ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
|
||||||
|
</script> |
||||||
|
<body> |
||||||
|
<a-scene photo-importer environment-by-url relaxing-introduction decompression-conclusion> |
||||||
|
<a-entity environment="preset: forest;"></a-entity> |
||||||
|
|
||||||
|
<a-entity sphere-collider="objects: a-box" super-hands hand-controls="left"></a-entity> |
||||||
|
<a-entity sphere-collider="objects: a-box" super-hands hand-controls="right"></a-entity> |
||||||
|
|
||||||
|
<!-- could be wrist watch as I've done via Twitter years ago --> |
||||||
|
<a-text watch id="timer" rotation="180 0 180" position="0 1 2" value="time"></a-text> |
||||||
|
|
||||||
|
<a-text id="instructions" position="-1 1.5 -0.65" value="welcome to your\n\nthink+relax space" opacity="0" |
||||||
|
animation__fadein="property: components.text.material.uniforms.opacity.value; to: 0.99; dur: 1500; easing: linear" |
||||||
|
animation__fadeout="property: components.text.material.uniforms.opacity.value; to: 0.01; dur: 1500; easing: linear; startEvents: fadeout;" |
||||||
|
animation__leave="property: object3D.position.z; to: -100; dur: 15000; easing: linear; delay: 2500;" |
||||||
|
></a-text> |
||||||
|
|
||||||
|
|
||||||
|
<a-text rotation="-20 20 0" position="-2 0.2 -2" scale="1 1 1" value="waiting"></a-text> |
||||||
|
<a-box material="wireframe:true" color="orange" position="-2 1 -2" scale="2 2 2"></a-box> |
||||||
|
<a-text rotation="-20 0 0" position=" 0 0.2 -2" scale="1 1 1" value="in progress"></a-text> |
||||||
|
<a-box material="wireframe:true" color="red" position="0 1 -2" scale="2 2 2"></a-box> |
||||||
|
<a-text rotation="-20 -20 0" position=" 2 0.2 -2" scale="1 1 1" value="completed"></a-text> |
||||||
|
<a-box material="wireframe:true" color="green" position="2 1 -2" scale="2 2 2"></a-box> |
||||||
|
|
||||||
|
<a-box material="shader:flat" id="placeholder1" hoverable grabbable stretchable draggable dropppable position="0.5 1.5 -0.5" scale="0.5 0.3 0.01"></a-box> |
||||||
|
<a-box material="shader:flat" id="placeholder2" hoverable grabbable stretchable draggable dropppable position="0 1.9 -0.7" scale="0.5 0.3 0.01"></a-box> |
||||||
|
<a-box material="shader:flat" id="placeholder3" hoverable grabbable stretchable draggable dropppable position="-0.5 1.5 -0.7" scale="0.5 0.3 0.01"></a-box> |
||||||
|
<!-- somehow creating dynamically fails... even though it did (!) work at some point (but wasn't saved via git)--> |
||||||
|
<!-- until then will rely on a pool of objects --> |
||||||
|
|
||||||
|
</a-scene> |
||||||
|
</body> |
@ -0,0 +1,187 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<!-- Required meta tags --> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||||
|
|
||||||
|
<!-- Bootstrap CSS --> |
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> |
||||||
|
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet"> |
||||||
|
|
||||||
|
<script src="clipboard.min.js"></script> |
||||||
|
<script src="upload/server/php/files/usersdb.js"></script> |
||||||
|
</head> |
||||||
|
<style> |
||||||
|
body { background-color: transparent; } |
||||||
|
|
||||||
|
#email, #sms { |
||||||
|
font-size: xx-large; |
||||||
|
} |
||||||
|
</style> |
||||||
|
|
||||||
|
<body> |
||||||
|
|
||||||
|
<img src="productlogo.png"> |
||||||
|
|
||||||
|
<script> |
||||||
|
var tickMark = "✔"; |
||||||
|
var uploadedURL = "upload/server/php/files/"; |
||||||
|
var images = [] |
||||||
|
var url = "/" |
||||||
|
var urlParameters = "?customimages=" |
||||||
|
|
||||||
|
function generateURL(){ |
||||||
|
//var url = "https://learnwebvr.xyz/importer.html" |
||||||
|
for (var image of images){ |
||||||
|
urlParameters += image + "," |
||||||
|
} |
||||||
|
console.log ("URL", urlParameters ); |
||||||
|
|
||||||
|
var encodedURL = url + urlParameters |
||||||
|
|
||||||
|
encodedURL += "&environment=" + document.querySelector("#environment").value |
||||||
|
document.querySelector("#link").href = encodedURL |
||||||
|
document.querySelector("#copyBtn").setAttribute("data-clipboard-text", encodedURL) |
||||||
|
document.querySelector("#generateURL").style.display = "block" |
||||||
|
|
||||||
|
document.querySelector("#email").href = "mailto:?&subject=session&body=" + encodeURIComponent( encodedURL ) |
||||||
|
// tested on iPhone 6S and Pixel2 and desktop |
||||||
|
|
||||||
|
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { |
||||||
|
} |
||||||
|
else { |
||||||
|
document.querySelector("#copydiv").className = "cold-md-6" |
||||||
|
document.querySelector("#emaildiv").className = "cold-md-6" |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
<div class="container"> |
||||||
|
|
||||||
|
<form class="form-signin"> |
||||||
|
|
||||||
|
Environment: |
||||||
|
<select id="environment"> |
||||||
|
<option name="contact">contact</option> |
||||||
|
<option name="egypt">egypt</option> |
||||||
|
<option name="checkerboard">checkerboard</option> |
||||||
|
<option name="forest" selected="selected">forest</option> |
||||||
|
<option name="goaland">goaland</option> |
||||||
|
<option name="yavapai">yavapai</option> |
||||||
|
<option name="goldmine">goldmine</option> |
||||||
|
<option name="threetowers">threetowers</option> |
||||||
|
<option name="poison">poison</option> |
||||||
|
<option name="arches">arches</option> |
||||||
|
<option name="tron">tron</option> |
||||||
|
<option name="japan">japan</option> |
||||||
|
<option name="dream">dream</option> |
||||||
|
<option name="volcano">volcano</option> |
||||||
|
<option name="starry">starry</option> |
||||||
|
<option name="osiris">osiris</option> |
||||||
|
</select> |
||||||
|
|
||||||
|
<button style="margin-top:2px;" type="button" onclick="unhide('#fileupload2');" class="btn btn-primary btn-block">Upload your office photos<br/>(post-its, white board, etc)</button> |
||||||
|
|
||||||
|
No content available? Try <a target="_blank" href="https://learnwebvr.xyz/?customimages=upload/server/php/files/photo6035074301452988871.jpg,upload/server/php/files/photo6035074301452988870.jpg,upload/server/php/files/IMG_6496 (1).jpg"> this example instead</a>! |
||||||
|
|
||||||
|
<input style="display:none;" id="fileupload2" type="file" name="files[]" data-url="upload/server/php/" multiple> |
||||||
|
<div style="display:none;" id="fileuploaded2">Files uploaded: </div> |
||||||
|
<div style="opacity:0.7;" id="uploadrate"></div> |
||||||
|
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> |
||||||
|
<script src="upload/js/vendor/jquery.ui.widget.js"></script> |
||||||
|
<script src="upload/js/jquery.iframe-transport.js"></script> |
||||||
|
<script src="upload/js/jquery.fileupload.js"></script> |
||||||
|
<script> |
||||||
|
|
||||||
|
function unhide( element ){ |
||||||
|
document.querySelector(element).style.display = "block"; |
||||||
|
} |
||||||
|
|
||||||
|
$(function () { |
||||||
|
|
||||||
|
$('#fileupload2').fileupload({ |
||||||
|
dataType: 'json', |
||||||
|
done: function (e, data) { |
||||||
|
$.each(data.result.files, function (index, file) { |
||||||
|
var imageURL = uploadedURL + (file.name) |
||||||
|
images.push(imageURL) |
||||||
|
var uploaded = document.querySelector("#fileuploaded2"); |
||||||
|
uploaded.style.display = "block"; |
||||||
|
uploaded.innerHTML += tickMark; |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
</script> |
||||||
|
|
||||||
|
<button style="margin-top:2px;" type="button" onclick="generateURL()" id="generate-simulation" class="btn btn-lg btn-primary btn-block">Generate your<br/>relax+think space</button> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div id="generateURL" style="display:none; text-align:center;" class="container"> |
||||||
|
<div class="container"> |
||||||
|
<a id="link" target="_blank" href=""><button type="button" class="btn btn-lg btn-primary btn-block">Launch relax+think space</button></a> |
||||||
|
</div> |
||||||
|
<br/> |
||||||
|
<div class="container"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-md-4" id="copydiv"> |
||||||
|
<button class="btn" data-clipboard-text="" id="copyBtn">Copy to clipboard</button> |
||||||
|
<script> |
||||||
|
new Clipboard('.btn'); |
||||||
|
</script> |
||||||
|
</div> |
||||||
|
<div class="col-md-4" id="emaildiv"> |
||||||
|
<a id="email" href="" target="_blank">email link</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<script> |
||||||
|
function login(){ |
||||||
|
document.querySelector("#login").style.display = "block" |
||||||
|
} |
||||||
|
function loginViaEmail(){ |
||||||
|
var email = document.querySelector("#useremail").value |
||||||
|
var userspace = database[email] |
||||||
|
if (!userspace){ |
||||||
|
document.querySelector("#nouser").style.display = "block" |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
document.querySelector("#spacesholder").style.display = "block" |
||||||
|
var spaces = document.querySelector("#spaces") |
||||||
|
var space = document.createElement("li") |
||||||
|
var spacelink = document.createElement("a") |
||||||
|
var images = userspace.files |
||||||
|
for (var image of images){ |
||||||
|
urlParameters += image.filename + "," |
||||||
|
} |
||||||
|
spacelink.href = url + urlParameters |
||||||
|
spacelink.target = "_blank" |
||||||
|
spacelink.innerHTML = userspace.last_login |
||||||
|
space.appendChild(spacelink) |
||||||
|
console.log(space) |
||||||
|
spaces.appendChild(space) |
||||||
|
console.log("userspace", userspace) |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<div>Already have an account? <span onclick="login()" style="text-decoration: underline;">Log-in</span> |
||||||
|
<form> |
||||||
|
<div id="login" style="display:none"><input id="useremail"/> |
||||||
|
<button style="margin-top:2px;" type="button" onclick="loginViaEmail()" id="loginemail" class="btn btn-lg btn-primary btn-block">Login</button> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
<div style="display:none" id="nouser">User not found. Double check your email address then contact fabien@iterative-explorations.com</a></div> |
||||||
|
<div style="display:none" id="spacesholder">Your spaces: |
||||||
|
<ul id="spaces"></ul> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<h3 style="position:absolute; bottom:0px; right:0px;">A product by <a href="https://iterative-explorations.com"><img width="200px" src="https://iterative-explorations.com/logo.svg"></a>.</h3> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,18 @@ |
|||||||
|
var database = { |
||||||
|
"fabien@benetou.fr" : { |
||||||
|
"username": "fabien", |
||||||
|
"password": "test", |
||||||
|
"last_login": 1576749381516, |
||||||
|
"environment": "forest", |
||||||
|
"files" : [ |
||||||
|
{ "position": "-2 1 -2", "filename" : "upload/server/php/files/C1C4E2F7-865A-4DB4-9274-1EA69B6C4A7E.jpeg" }, |
||||||
|
{ "position": "0 1 -2", "filename" : "upload/server/php/files/4A21A76C-F375-4884-9040-BC51A24057A6.jpeg" }, |
||||||
|
{ "position": "2 1 -2", "filename" : "upload/server/php/files/2CD9932F-891C-4BA5-BDDA-A3B39B39CF09.jpeg" } |
||||||
|
], |
||||||
|
"categories" : [ |
||||||
|
{ "position": "-2 1 -2", "color" : "orange", "label" : "waiting" }, |
||||||
|
{ "position": "0 1 -2", "color" : "red", "label" : "in progress" }, |
||||||
|
{ "position": "2 1 -2", "color" : "green", "label" : "completed" } |
||||||
|
] |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue