You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

621 lines
16 KiB

  1. {% extends 'standard_layout.html.twig' %}
  2. {% block stylesheets %}
  3. {{ parent() }}
  4. <link rel="stylesheet" href="{{ asset("/css/nouislider.css") }}">
  5. <style>
  6. body {
  7. margin-top: 30px;
  8. margin-bottom: 30px;
  9. background: #f2f2f2;
  10. }
  11. .canvas-container {
  12. margin-right: auto;
  13. margin-left: auto;
  14. }
  15. </style>
  16. {% endblock %}
  17. {% block javascripts %}
  18. {{ parent() }}
  19. <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.11/fabric.min.js"></script>
  20. <script src="{{ asset("/js/nouislider.js") }}"></script>
  21. <script>
  22. let canvas
  23. let number
  24. const grid = 30
  25. const backgroundColor = '#f8f8f8'
  26. const lineStroke = '#ebebeb'
  27. const tableFill = 'rgba(150, 111, 51, 0.7)'
  28. const tableStroke = '#694d23'
  29. const tableShadow = 'rgba(0, 0, 0, 0.4) 3px 3px 7px'
  30. const chairFill = 'rgba(67, 42, 4, 0.7)'
  31. const chairStroke = '#32230b'
  32. const chairShadow = 'rgba(0, 0, 0, 0.4) 3px 3px 7px'
  33. const barFill = 'rgba(0, 93, 127, 0.7)'
  34. const barStroke = '#003e54'
  35. const barShadow = 'rgba(0, 0, 0, 0.4) 3px 3px 7px'
  36. const barText = 'Bar'
  37. const wallFill = 'rgba(136, 136, 136, 0.7)'
  38. const wallStroke = '#686868'
  39. const wallShadow = 'rgba(0, 0, 0, 0.4) 5px 5px 20px'
  40. var photoUrlLandscape = 'https://images8.alphacoders.com/292/292379.jpg',
  41. photoUrlPortrait = 'https://presspack.rte.ie/wp-content/blogs.dir/2/files/2015/04/AMC_TWD_Maggie_Portraits_4817_V1.jpg'
  42. let widthEl = document.getElementById('width')
  43. let heightEl = document.getElementById('height')
  44. let canvasEl = document.getElementById('canvas')
  45. function initCanvas() {
  46. if (canvas) {
  47. canvas.clear()
  48. canvas.dispose()
  49. }
  50. canvas = new fabric.Canvas('canvas')
  51. number = 1
  52. canvas.backgroundColor = backgroundColor
  53. // canvas.setBackgroundImage('https://presspack.rte.ie/wp-content/blogs.dir/2/files/2015/04/AMC_TWD_Maggie_Portraits_4817_V1.jpg', canvas.renderAll.bind(canvas));
  54. for (let i = 0; i < (canvas.height / grid); i++) {
  55. const lineX = new fabric.Line([ 0, i * grid, canvas.height, i * grid], {
  56. stroke: lineStroke,
  57. selectable: false,
  58. type: 'line'
  59. })
  60. const lineY = new fabric.Line([ i * grid, 0, i * grid, canvas.height], {
  61. stroke: lineStroke,
  62. selectable: false,
  63. type: 'line'
  64. })
  65. sendLinesToBack()
  66. canvas.add(lineX)
  67. canvas.add(lineY)
  68. }
  69. canvas.on('object:moving', function(e) {
  70. snapToGrid(e.target)
  71. })
  72. canvas.on('object:scaling', function(e) {
  73. if (e.target.scaleX > 5) {
  74. e.target.scaleX = 5
  75. }
  76. if (e.target.scaleY > 5) {
  77. e.target.scaleY = 5
  78. }
  79. if (!e.target.strokeWidthUnscaled && e.target.strokeWidth) {
  80. e.target.strokeWidthUnscaled = e.target.strokeWidth
  81. }
  82. if (e.target.strokeWidthUnscaled) {
  83. e.target.strokeWidth = e.target.strokeWidthUnscaled / e.target.scaleX
  84. if (e.target.strokeWidth === e.target.strokeWidthUnscaled) {
  85. e.target.strokeWidth = e.target.strokeWidthUnscaled / e.target.scaleY
  86. }
  87. }
  88. })
  89. canvas.on('object:modified', function(e) {
  90. e.target.scaleX = e.target.scaleX >= 0.25 ? (Math.round(e.target.scaleX * 2) / 2) : 0.5
  91. e.target.scaleY = e.target.scaleY >= 0.25 ? (Math.round(e.target.scaleY * 2) / 2) : 0.5
  92. snapToGrid(e.target)
  93. if (e.target.type === 'table') {
  94. canvas.bringToFront(e.target)
  95. }
  96. else {
  97. canvas.sendToBack(e.target)
  98. }
  99. sendLinesToBack()
  100. })
  101. canvas.observe('object:moving', function (e) {
  102. checkBoudningBox(e)
  103. })
  104. canvas.observe('object:rotating', function (e) {
  105. checkBoudningBox(e)
  106. })
  107. canvas.observe('object:scaling', function (e) {
  108. checkBoudningBox(e)
  109. })
  110. }
  111. $( document ).ready(function() {
  112. let widthEl = document.getElementById('width')
  113. let heightEl = document.getElementById('height')
  114. let canvasEl = document.getElementById('canvas')
  115. initCanvas();
  116. document.querySelectorAll('.rectangle')[0].addEventListener('click', function() {
  117. const o = addRect(0, 0, 60, 60)
  118. canvas.setActiveObject(o)
  119. });
  120. document.querySelectorAll('.circle')[0].addEventListener('click', function() {
  121. const o = addCircle(0, 0, 30)
  122. canvas.setActiveObject(o)
  123. });
  124. document.querySelectorAll('.triangle')[0].addEventListener('click', function() {
  125. const o = addTriangle(0, 0, 30)
  126. canvas.setActiveObject(o)
  127. });
  128. document.querySelectorAll('.chair')[0].addEventListener('click', function() {
  129. const o = addChair(0, 0)
  130. canvas.setActiveObject(o)
  131. });
  132. document.querySelectorAll('.bar')[0].addEventListener('click', function() {
  133. const o = addBar(0, 0, 180, 60)
  134. canvas.setActiveObject(o)
  135. });
  136. document.querySelectorAll('.wall')[0].addEventListener('click', function() {
  137. const o = addWall(0, 0, 60, 180)
  138. canvas.setActiveObject(o)
  139. });
  140. document.querySelectorAll('.remove')[0].addEventListener('click', function() {
  141. const o = canvas.getActiveObject()
  142. if (o) {
  143. o.remove()
  144. canvas.remove(o)
  145. canvas.discardActiveObject()
  146. canvas.renderAll()
  147. }
  148. });
  149. document.querySelectorAll('.customer-mode')[0].addEventListener('click', function() {
  150. canvas.getObjects().map(o => {
  151. o.hasControls = false
  152. o.lockMovementX = true
  153. o.lockMovementY = true
  154. if (o.type === 'chair' || o.type === 'bar' || o.type === 'wall') {
  155. o.selectable = false
  156. }
  157. o.borderColor = '#38A62E'
  158. o.borderScaleFactor = 2.5
  159. })
  160. canvas.selection = false
  161. canvas.hoverCursor = 'pointer'
  162. canvas.discardActiveObject()
  163. canvas.renderAll()
  164. document.querySelectorAll('.admin-menu')[0].style.display = 'none'
  165. document.querySelectorAll('.customer-menu')[0].style.display = 'block'
  166. });
  167. document.querySelectorAll('.admin-mode')[0].addEventListener('click', function() {
  168. canvas.getObjects().map(o => {
  169. o.hasControls = true
  170. o.lockMovementX = false
  171. o.lockMovementY = false
  172. if (o.type === 'chair' || o.type === 'bar' || o.type === 'wall') {
  173. o.selectable = true
  174. }
  175. o.borderColor = 'rgba(102, 153, 255, 0.75)'
  176. o.borderScaleFactor = 1
  177. })
  178. canvas.selection = true
  179. canvas.hoverCursor = 'move'
  180. canvas.discardActiveObject()
  181. canvas.renderAll()
  182. document.querySelectorAll('.admin-menu')[0].style.display = 'block'
  183. document.querySelectorAll('.customer-menu')[0].style.display = 'none'
  184. });
  185. document.querySelectorAll('.submit')[0].addEventListener('click', function() {
  186. const obj = canvas.getActiveObject()
  187. $('#modal').modal('show')
  188. let modalText = 'You have not selected anything'
  189. if (obj) {
  190. modalText = 'You have selected table ' + obj.number + ', time: ' + formatTime(slider.noUiSlider.get())
  191. }
  192. document.querySelectorAll('#modal-table-id')[0].innerHTML = modalText
  193. })
  194. const slider = document.getElementById('slider')
  195. noUiSlider.create(slider, {
  196. start: 1200,
  197. step: 15,
  198. connect: 'lower',
  199. range: {
  200. min: 0,
  201. max: 1425
  202. }
  203. })
  204. const sliderValue = document.getElementById('slider-value')
  205. slider.noUiSlider.on('update', function(values, handle) {
  206. sliderValue.innerHTML = formatTime(values[handle])
  207. });
  208. addDefaultObjects();
  209. widthEl.addEventListener('change', () => {
  210. resizeCanvas()
  211. initCanvas()
  212. addDefaultObjects()
  213. })
  214. heightEl.addEventListener('change', () => {
  215. resizeCanvas()
  216. initCanvas()
  217. addDefaultObjects()
  218. });
  219. resizeCanvas();
  220. });
  221. function resizeCanvas() {
  222. widthEl = document.getElementById('width')
  223. heightEl = document.getElementById('height')
  224. canvasEl = document.getElementById('canvas')
  225. canvasEl.width = widthEl.value ? widthEl.value : 1024
  226. canvasEl.height = heightEl.value ? heightEl.value : 812
  227. const canvasContainerEl = document.querySelectorAll('.canvas-container')[0]
  228. canvasContainerEl.style.width = canvasEl.width
  229. canvasContainerEl.style.height = canvasEl.height
  230. }
  231. function generateId() {
  232. return Math.random().toString(36).substr(2, 8)
  233. }
  234. function addRect(left, top, width, height) {
  235. const id = generateId()
  236. const o = new fabric.Rect({
  237. width: width,
  238. height: height,
  239. fill: tableFill,
  240. stroke: tableStroke,
  241. strokeWidth: 2,
  242. shadow: tableShadow,
  243. originX: 'center',
  244. originY: 'center',
  245. centeredRotation: true,
  246. snapAngle: 45,
  247. selectable: true
  248. })
  249. const t = new fabric.IText(number.toString(), {
  250. fontFamily: 'Calibri',
  251. fontSize: 14,
  252. fill: '#fff',
  253. textAlign: 'center',
  254. originX: 'center',
  255. originY: 'center'
  256. })
  257. const g = new fabric.Group([o, t], {
  258. left: left,
  259. top: top,
  260. centeredRotation: true,
  261. snapAngle: 45,
  262. selectable: true,
  263. type: 'table',
  264. id: id,
  265. number: number
  266. })
  267. canvas.add(g)
  268. number++
  269. return g
  270. }
  271. function addCircle(left, top, radius) {
  272. const id = generateId()
  273. const o = new fabric.Circle({
  274. radius: radius,
  275. fill: tableFill,
  276. stroke: tableStroke,
  277. strokeWidth: 2,
  278. shadow: tableShadow,
  279. originX: 'center',
  280. originY: 'center',
  281. centeredRotation: true
  282. })
  283. const t = new fabric.IText(number.toString(), {
  284. fontFamily: 'Calibri',
  285. fontSize: 14,
  286. fill: '#fff',
  287. textAlign: 'center',
  288. originX: 'center',
  289. originY: 'center'
  290. })
  291. const g = new fabric.Group([o, t], {
  292. left: left,
  293. top: top,
  294. centeredRotation: true,
  295. snapAngle: 45,
  296. selectable: true,
  297. type: 'table',
  298. id: id,
  299. number: number
  300. })
  301. canvas.add(g)
  302. number++
  303. return g
  304. }
  305. function addTriangle(left, top, radius) {
  306. const id = generateId()
  307. const o = new fabric.Triangle({
  308. radius: radius,
  309. fill: tableFill,
  310. stroke: tableStroke,
  311. strokeWidth: 2,
  312. shadow: tableShadow,
  313. originX: 'center',
  314. originY: 'center',
  315. centeredRotation: true
  316. })
  317. const t = new fabric.IText(number.toString(), {
  318. fontFamily: 'Calibri',
  319. fontSize: 14,
  320. fill: '#fff',
  321. textAlign: 'center',
  322. originX: 'center',
  323. originY: 'center'
  324. })
  325. const g = new fabric.Group([o, t], {
  326. left: left,
  327. top: top,
  328. centeredRotation: true,
  329. snapAngle: 45,
  330. selectable: true,
  331. type: 'table',
  332. id: id,
  333. number: number
  334. })
  335. canvas.add(g)
  336. number++
  337. return g
  338. }
  339. function addChair(left, top, width, height) {
  340. const o = new fabric.Rect({
  341. left: left,
  342. top: top,
  343. width: 30,
  344. height: 30,
  345. fill: chairFill,
  346. stroke: chairStroke,
  347. strokeWidth: 2,
  348. shadow: chairShadow,
  349. originX: 'left',
  350. originY: 'top',
  351. centeredRotation: true,
  352. snapAngle: 45,
  353. selectable: true,
  354. type: 'chair',
  355. id: generateId()
  356. })
  357. canvas.add(o)
  358. return o
  359. }
  360. function addBar(left, top, width, height) {
  361. const o = new fabric.Rect({
  362. width: width,
  363. height: height,
  364. fill: barFill,
  365. stroke: barStroke,
  366. strokeWidth: 2,
  367. shadow: barShadow,
  368. originX: 'center',
  369. originY: 'center',
  370. type: 'bar',
  371. id: generateId()
  372. })
  373. const t = new fabric.IText(barText, {
  374. fontFamily: 'Calibri',
  375. fontSize: 14,
  376. fill: '#fff',
  377. textAlign: 'center',
  378. originX: 'center',
  379. originY: 'center'
  380. })
  381. const g = new fabric.Group([o, t], {
  382. left: left,
  383. top: top,
  384. centeredRotation: true,
  385. snapAngle: 45,
  386. selectable: true,
  387. type: 'bar'
  388. })
  389. canvas.add(g)
  390. return g
  391. }
  392. function addWall(left, top, width, height) {
  393. const o = new fabric.Rect({
  394. left: left,
  395. top: top,
  396. width: width,
  397. height: height,
  398. fill: wallFill,
  399. stroke: wallStroke,
  400. strokeWidth: 2,
  401. shadow: wallShadow,
  402. originX: 'left',
  403. originY: 'top',
  404. centeredRotation: true,
  405. snapAngle: 45,
  406. selectable: true,
  407. type: 'wall',
  408. id: generateId()
  409. })
  410. canvas.add(o)
  411. return o
  412. }
  413. function snapToGrid(target) {
  414. target.set({
  415. left: Math.round(target.left / (grid / 2)) * grid / 2,
  416. top: Math.round(target.top / (grid / 2)) * grid / 2
  417. })
  418. }
  419. function checkBoudningBox(e) {
  420. const obj = e.target
  421. if (!obj) {
  422. return
  423. }
  424. obj.setCoords()
  425. const objBoundingBox = obj.getBoundingRect()
  426. if (objBoundingBox.top < 0) {
  427. obj.set('top', 0)
  428. obj.setCoords()
  429. }
  430. if (objBoundingBox.left > canvas.width - objBoundingBox.width) {
  431. obj.set('left', canvas.width - objBoundingBox.width)
  432. obj.setCoords()
  433. }
  434. if (objBoundingBox.top > canvas.height - objBoundingBox.height) {
  435. obj.set('top', canvas.height - objBoundingBox.height)
  436. obj.setCoords()
  437. }
  438. if (objBoundingBox.left < 0) {
  439. obj.set('left', 0)
  440. obj.setCoords()
  441. }
  442. }
  443. function sendLinesToBack() {
  444. canvas.getObjects().map(o => {
  445. if (o.type === 'line') {
  446. canvas.sendToBack(o)
  447. }
  448. })
  449. }
  450. function formatTime(val) {
  451. const hours = Math.floor(val / 60)
  452. const minutes = val % 60
  453. const englishHours = hours > 12 ? hours - 12 : hours
  454. const normal = hours + ':' + minutes + (minutes === 0 ? '0' : '')
  455. const english = englishHours + ':' + minutes + (minutes === 0 ? '0' : '') + ' ' + (hours > 12 ? 'PM' : 'AM')
  456. return normal + ' (' + english + ')'
  457. }
  458. function addDefaultObjects() {
  459. addChair(15, 105)
  460. addChair(15, 135)
  461. addChair(75, 105)
  462. addChair(75, 135)
  463. addChair(225, 75)
  464. addChair(255, 75)
  465. addChair(225, 135)
  466. addChair(255, 135)
  467. addChair(225, 195)
  468. addChair(255, 195)
  469. addChair(225, 255)
  470. addChair(255, 255)
  471. addChair(15, 195)
  472. addChair(45, 195)
  473. addChair(15, 255)
  474. addChair(45, 255)
  475. addChair(15, 315)
  476. addChair(45, 315)
  477. addChair(15, 375)
  478. addChair(45, 375)
  479. addChair(225, 315)
  480. addChair(255, 315)
  481. addChair(225, 375)
  482. addChair(255, 375)
  483. addChair(15, 435)
  484. addChair(15, 495)
  485. addChair(15, 555)
  486. addChair(15, 615)
  487. addChair(225, 615)
  488. addChair(255, 615)
  489. addChair(195, 495)
  490. addChair(195, 525)
  491. addChair(255, 495)
  492. addChair(255, 525)
  493. addChair(225, 675)
  494. addChair(255, 675)
  495. addRect(30, 90, 60, 90)
  496. addRect(210, 90, 90, 60)
  497. addRect(210, 210, 90, 60)
  498. addRect(0, 210, 90, 60)
  499. addRect(0, 330, 90, 60)
  500. addRect(210, 330, 90, 60)
  501. addRect(0, 450, 60, 60)
  502. addRect(0, 570, 60, 60)
  503. addRect(210, 480, 60, 90)
  504. addRect(210, 630, 90, 60)
  505. addBar(120, 0, 180, 60)
  506. addWall(120, 510, 60, 60)
  507. }
  508. </script>
  509. {% endblock %}
  510. {% block sonata_page_content %}
  511. <div class="container-fluid text-center">
  512. <h1>Gestión de espacios</h1>
  513. <div class="form-group admin-menu">
  514. <div class="row">
  515. <div class="col-sm-2 col-sm-offset-3 form-group">
  516. <label>Ancho (px)</label>
  517. <input type="number" id="width" class="form-control" value="1024" />
  518. </div>
  519. <div class="col-sm-2 form-group">
  520. <label>Alto (px)</label>
  521. <input type="number" id="height" class="form-control" value="812" />
  522. </div>
  523. <div class="col-sm-2 form-group">
  524. <label>&nbsp;</label>
  525. <br />
  526. <button class="btn btn-primary">Guardar</button>
  527. </div>
  528. </div>
  529. <div class="btn-group">
  530. <button class="btn btn-primary rectangle">+ &#9647; Mesa</button>
  531. <button class="btn btn-primary circle">+ &#9711; Mesa</button>
  532. <button class="btn btn-primary triangle">+ &#9651; Mesa</button>
  533. <button class="btn btn-primary chair">+ Silla</button>
  534. <button class="btn btn-primary bar">+ Bar</button>
  535. <button class="btn btn-default wall">+ Muro</button>
  536. <button class="btn btn-danger remove">Borrar</button>
  537. <button class="btn btn-warning customer-mode">Modo Reserva</button>
  538. </div>
  539. </div>
  540. <div class="form-group customer-menu" style="display: none;">
  541. <div class="btn-group">
  542. <button class="btn btn-success submit">Enviar reserva</button>
  543. <button class="btn btn-warning admin-mode">Modo diseño</button>
  544. </div>
  545. <br />
  546. <br />
  547. <div id="slider"></div>
  548. <div id="slider-value"></div>
  549. </div>
  550. <canvas id="canvas" width="1024" height="812"></canvas>
  551. </div>
  552. <div class="modal fade" id="modal" tabindex="-1" role="dialog">
  553. <div class="modal-dialog" role="document">
  554. <div class="modal-content">
  555. <div class="modal-body text-center">
  556. <p id="modal-table-id"></p>
  557. </div>
  558. <div class="modal-footer">
  559. <button type="button" class="btn btn-default" data-dismiss="modal">OK</button>
  560. </div>
  561. </div>
  562. </div>
  563. </div>
  564. {% endblock sonata_page_content %}