module_shop.go 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273
  1. package modules
  2. import (
  3. "html"
  4. "net/http"
  5. "strings"
  6. "golang-fave/assets"
  7. "golang-fave/consts"
  8. "golang-fave/engine/builder"
  9. "golang-fave/engine/fetdata"
  10. "golang-fave/engine/sqlw"
  11. "golang-fave/engine/wrapper"
  12. "golang-fave/utils"
  13. )
  14. func (this *Modules) shop_GetCurrencySelectOptions(wrap *wrapper.Wrapper, id int) string {
  15. result := ``
  16. rows, err := wrap.DB.Query(
  17. `SELECT
  18. id,
  19. code
  20. FROM
  21. shop_currencies
  22. ORDER BY
  23. id ASC
  24. ;`,
  25. )
  26. if err == nil {
  27. defer rows.Close()
  28. values := make([]string, 2)
  29. scan := make([]interface{}, len(values))
  30. for i := range values {
  31. scan[i] = &values[i]
  32. }
  33. idStr := utils.IntToStr(id)
  34. for rows.Next() {
  35. err = rows.Scan(scan...)
  36. if err == nil {
  37. selected := ""
  38. if string(values[0]) == idStr {
  39. selected = " selected"
  40. }
  41. result += `<option title="` + html.EscapeString(string(values[1])) + `" value="` + html.EscapeString(string(values[0])) + `"` + selected + `>` + html.EscapeString(string(values[1])) + `</option>`
  42. }
  43. }
  44. }
  45. return result
  46. }
  47. func (this *Modules) shop_GetProductValuesInputs(wrap *wrapper.Wrapper, product_id int) string {
  48. result := ``
  49. rows, err := wrap.DB.Query(
  50. `SELECT
  51. shop_filters.id,
  52. shop_filters.name,
  53. shop_filters_values.id,
  54. shop_filters_values.name,
  55. IF(shop_filter_product_values.filter_value_id > 0, 1, 0) as selected
  56. FROM
  57. shop_filters
  58. LEFT JOIN shop_filters_values ON shop_filters_values.filter_id = shop_filters.id
  59. LEFT JOIN shop_filter_product_values ON shop_filter_product_values.filter_value_id = shop_filters_values.id
  60. LEFT JOIN (
  61. SELECT
  62. shop_filters_values.filter_id,
  63. shop_filter_product_values.product_id
  64. FROM
  65. shop_filter_product_values
  66. LEFT JOIN shop_filters_values ON shop_filters_values.id = shop_filter_product_values.filter_value_id
  67. WHERE
  68. shop_filter_product_values.product_id = ` + utils.IntToStr(product_id) + `
  69. GROUP BY
  70. shop_filters_values.filter_id
  71. ) as filter_used ON filter_used.filter_id = shop_filters.id
  72. WHERE
  73. (
  74. shop_filter_product_values.product_id = ` + utils.IntToStr(product_id) + ` OR
  75. shop_filter_product_values.product_id IS NULL
  76. ) AND
  77. filter_used.filter_id IS NOT NULL
  78. ORDER BY
  79. shop_filters.name ASC,
  80. shop_filters_values.name ASC
  81. ;`,
  82. )
  83. filter_ids := []int{}
  84. filter_names := map[int]string{}
  85. filter_values := map[int][]string{}
  86. if err == nil {
  87. defer rows.Close()
  88. values := make([]string, 5)
  89. scan := make([]interface{}, len(values))
  90. for i := range values {
  91. scan[i] = &values[i]
  92. }
  93. for rows.Next() {
  94. err = rows.Scan(scan...)
  95. if err == nil {
  96. filter_id := utils.StrToInt(string(values[0]))
  97. if !utils.InArrayInt(filter_ids, filter_id) {
  98. filter_ids = append(filter_ids, filter_id)
  99. }
  100. filter_names[filter_id] = html.EscapeString(string(values[1]))
  101. selected := ``
  102. if utils.StrToInt(string(values[4])) == 1 {
  103. selected = ` selected`
  104. }
  105. filter_values[filter_id] = append(filter_values[filter_id], `<option value="`+html.EscapeString(string(values[2]))+`"`+selected+`>`+html.EscapeString(string(values[3]))+`</option>`)
  106. }
  107. }
  108. }
  109. for _, filter_id := range filter_ids {
  110. result += `<div class="form-group" id="prod_attr_` + utils.IntToStr(filter_id) + `">` +
  111. `<div><b>` + filter_names[filter_id] + `</b></div>` +
  112. `<div class="position-relative">` +
  113. `<select class="selectpicker form-control" name="value.` + utils.IntToStr(filter_id) + `" autocomplete="off" required multiple>` +
  114. strings.Join(filter_values[filter_id], "") +
  115. `</select>` +
  116. `<button type="button" class="btn btn-danger btn-dynamic-remove" onclick="fave.ShopProductsRemove(this);">&times;</button>` +
  117. `</div>` +
  118. `</div>`
  119. }
  120. return result
  121. }
  122. func (this *Modules) shop_GetFilterValuesInputs(wrap *wrapper.Wrapper, filter_id int) string {
  123. result := ``
  124. rows, err := wrap.DB.Query(
  125. `SELECT
  126. id,
  127. name
  128. FROM
  129. shop_filters_values
  130. WHERE
  131. filter_id = ?
  132. ORDER BY
  133. name ASC
  134. ;`,
  135. filter_id,
  136. )
  137. if err == nil {
  138. defer rows.Close()
  139. values := make([]string, 2)
  140. scan := make([]interface{}, len(values))
  141. for i := range values {
  142. scan[i] = &values[i]
  143. }
  144. for rows.Next() {
  145. err = rows.Scan(scan...)
  146. if err == nil {
  147. result += `<div class="form-group position-relative"><input class="form-control" type="text" name="value.` + html.EscapeString(string(values[0])) + `" value="` + html.EscapeString(string(values[1])) + `" placeholder="" autocomplete="off" required><button type="button" class="btn btn-danger btn-dynamic-remove" onclick="fave.ShopAttributesRemove(this);">&times;</button></div>`
  148. }
  149. }
  150. }
  151. return result
  152. }
  153. func (this *Modules) shop_GetAllAttributesSelectOptions(wrap *wrapper.Wrapper) string {
  154. result := ``
  155. rows, err := wrap.DB.Query(
  156. `SELECT
  157. id,
  158. name,
  159. filter
  160. FROM
  161. shop_filters
  162. ORDER BY
  163. name ASC
  164. ;`,
  165. )
  166. result += `<option title="&mdash;" value="0">&mdash;</option>`
  167. if err == nil {
  168. defer rows.Close()
  169. values := make([]string, 3)
  170. scan := make([]interface{}, len(values))
  171. for i := range values {
  172. scan[i] = &values[i]
  173. }
  174. for rows.Next() {
  175. err = rows.Scan(scan...)
  176. if err == nil {
  177. result += `<option title="` + html.EscapeString(string(values[1])) + `" value="` + html.EscapeString(string(values[0])) + `">` + html.EscapeString(string(values[1])) + `</option>`
  178. }
  179. }
  180. }
  181. return result
  182. }
  183. func (this *Modules) shop_GetAllCurrencies(wrap *wrapper.Wrapper) map[int]string {
  184. result := map[int]string{}
  185. rows, err := wrap.DB.Query(
  186. `SELECT
  187. id,
  188. code
  189. FROM
  190. shop_currencies
  191. ORDER BY
  192. id ASC
  193. ;`,
  194. )
  195. if err == nil {
  196. defer rows.Close()
  197. values := make([]string, 2)
  198. scan := make([]interface{}, len(values))
  199. for i := range values {
  200. scan[i] = &values[i]
  201. }
  202. for rows.Next() {
  203. err = rows.Scan(scan...)
  204. if err == nil {
  205. result[utils.StrToInt(string(values[0]))] = html.EscapeString(string(values[1]))
  206. }
  207. }
  208. }
  209. return result
  210. }
  211. func (this *Modules) RegisterModule_Shop() *Module {
  212. return this.newModule(MInfo{
  213. WantDB: true,
  214. Mount: "shop",
  215. Name: "Shop",
  216. Order: 2,
  217. System: false,
  218. Icon: assets.SysSvgIconList,
  219. Sub: &[]MISub{
  220. {Mount: "default", Name: "List of products", Show: true, Icon: assets.SysSvgIconList},
  221. {Mount: "add", Name: "Add new product", Show: true, Icon: assets.SysSvgIconPlus},
  222. {Mount: "modify", Name: "Modify product", Show: false},
  223. {Sep: true, Show: true},
  224. {Mount: "categories", Name: "List of categories", Show: true, Icon: assets.SysSvgIconList},
  225. {Mount: "categories-add", Name: "Add new category", Show: true, Icon: assets.SysSvgIconPlus},
  226. {Mount: "categories-modify", Name: "Modify category", Show: false},
  227. {Sep: true, Show: true},
  228. {Mount: "attributes", Name: "List of attributes", Show: true, Icon: assets.SysSvgIconList},
  229. {Mount: "attributes-add", Name: "Add new attribute", Show: true, Icon: assets.SysSvgIconPlus},
  230. {Mount: "attributes-modify", Name: "Modify attribute", Show: false},
  231. {Sep: true, Show: true},
  232. {Mount: "currencies", Name: "List of currencies", Show: true, Icon: assets.SysSvgIconList},
  233. {Mount: "currencies-add", Name: "Add new currency", Show: true, Icon: assets.SysSvgIconPlus},
  234. {Mount: "currencies-modify", Name: "Modify currency", Show: false},
  235. },
  236. }, func(wrap *wrapper.Wrapper) {
  237. if len(wrap.UrlArgs) == 3 && wrap.UrlArgs[0] == "shop" && wrap.UrlArgs[1] == "category" && wrap.UrlArgs[2] != "" {
  238. // Shop category
  239. row := &utils.MySql_shop_category{}
  240. err := wrap.DB.QueryRow(`
  241. SELECT
  242. id,
  243. user,
  244. name,
  245. alias,
  246. lft,
  247. rgt
  248. FROM
  249. shop_cats
  250. WHERE
  251. alias = ? AND
  252. id > 1
  253. LIMIT 1;`,
  254. wrap.UrlArgs[2],
  255. ).Scan(
  256. &row.A_id,
  257. &row.A_user,
  258. &row.A_name,
  259. &row.A_alias,
  260. &row.A_lft,
  261. &row.A_rgt,
  262. )
  263. if err != nil && err != wrapper.ErrNoRows {
  264. // System error 500
  265. utils.SystemErrorPageEngine(wrap.W, err)
  266. return
  267. } else if err == wrapper.ErrNoRows {
  268. // User error 404 page
  269. wrap.RenderFrontEnd("404", fetdata.New(wrap, nil, true), http.StatusNotFound)
  270. return
  271. }
  272. // Fix url
  273. if wrap.R.URL.Path[len(wrap.R.URL.Path)-1] != '/' {
  274. http.Redirect(wrap.W, wrap.R, wrap.R.URL.Path+"/"+utils.ExtractGetParams(wrap.R.RequestURI), 301)
  275. return
  276. }
  277. // Render template
  278. wrap.RenderFrontEnd("shop-category", fetdata.New(wrap, row, false), http.StatusOK)
  279. return
  280. } else if len(wrap.UrlArgs) == 2 && wrap.UrlArgs[0] == "shop" && wrap.UrlArgs[1] != "" {
  281. // Shop product
  282. row := &utils.MySql_shop_product{}
  283. err := wrap.DB.QueryRow(`
  284. SELECT
  285. id,
  286. user,
  287. currency,
  288. price,
  289. name,
  290. alias,
  291. briefly,
  292. content,
  293. UNIX_TIMESTAMP(datetime) as datetime,
  294. active
  295. FROM
  296. shop_products
  297. WHERE
  298. active = 1 and
  299. alias = ?
  300. LIMIT 1;`,
  301. wrap.UrlArgs[1],
  302. ).Scan(
  303. &row.A_id,
  304. &row.A_user,
  305. &row.A_currency,
  306. &row.A_price,
  307. &row.A_name,
  308. &row.A_alias,
  309. &row.A_briefly,
  310. &row.A_content,
  311. &row.A_datetime,
  312. &row.A_active,
  313. )
  314. if err != nil && err != wrapper.ErrNoRows {
  315. // System error 500
  316. utils.SystemErrorPageEngine(wrap.W, err)
  317. return
  318. } else if err == wrapper.ErrNoRows {
  319. // User error 404 page
  320. wrap.RenderFrontEnd("404", fetdata.New(wrap, nil, true), http.StatusNotFound)
  321. return
  322. }
  323. // Fix url
  324. if wrap.R.URL.Path[len(wrap.R.URL.Path)-1] != '/' {
  325. http.Redirect(wrap.W, wrap.R, wrap.R.URL.Path+"/"+utils.ExtractGetParams(wrap.R.RequestURI), 301)
  326. return
  327. }
  328. // Render template
  329. wrap.RenderFrontEnd("shop-product", fetdata.New(wrap, row, false), http.StatusOK)
  330. return
  331. } else if len(wrap.UrlArgs) == 1 && wrap.UrlArgs[0] == "shop" {
  332. // Shop
  333. // Fix url
  334. if wrap.R.URL.Path[len(wrap.R.URL.Path)-1] != '/' {
  335. http.Redirect(wrap.W, wrap.R, wrap.R.URL.Path+"/"+utils.ExtractGetParams(wrap.R.RequestURI), 301)
  336. return
  337. }
  338. // Render template
  339. wrap.RenderFrontEnd("shop", fetdata.New(wrap, nil, false), http.StatusOK)
  340. return
  341. }
  342. // User error 404 page
  343. wrap.RenderFrontEnd("404", fetdata.New(wrap, nil, true), http.StatusNotFound)
  344. }, func(wrap *wrapper.Wrapper) (string, string, string) {
  345. content := ""
  346. sidebar := ""
  347. if wrap.CurrSubModule == "" || wrap.CurrSubModule == "default" {
  348. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  349. {Name: "List of products"},
  350. })
  351. // Load currencies
  352. currencies := this.shop_GetAllCurrencies(wrap)
  353. content += builder.DataTable(
  354. wrap,
  355. "shop_products",
  356. "id",
  357. "DESC",
  358. &[]builder.DataTableRow{
  359. {
  360. DBField: "id",
  361. },
  362. {
  363. DBField: "name",
  364. NameInTable: "Product / URL",
  365. CallBack: func(values *[]string) string {
  366. name := `<a href="/cp/` + wrap.CurrModule + `/modify/` + (*values)[0] + `/">` + html.EscapeString((*values)[1]) + `</a>`
  367. alias := html.EscapeString((*values)[2])
  368. return `<div>` + name + `</div><div><small>/shop/` + alias + `/</small></div>`
  369. },
  370. },
  371. {
  372. DBField: "alias",
  373. },
  374. {
  375. DBField: "currency",
  376. },
  377. {
  378. DBField: "price",
  379. NameInTable: "Price",
  380. Classes: "d-none d-md-table-cell",
  381. CallBack: func(values *[]string) string {
  382. return `<div>` + utils.Float64ToStr(utils.StrToFloat64((*values)[4])) + `</div>` +
  383. `<div><small>` + currencies[utils.StrToInt((*values)[3])] + `</small></div>`
  384. },
  385. },
  386. {
  387. DBField: "datetime",
  388. DBExp: "UNIX_TIMESTAMP(`datetime`)",
  389. NameInTable: "Date / Time",
  390. Classes: "d-none d-lg-table-cell",
  391. CallBack: func(values *[]string) string {
  392. t := int64(utils.StrToInt((*values)[5]))
  393. return `<div>` + utils.UnixTimestampToFormat(t, "02.01.2006") + `</div>` +
  394. `<div><small>` + utils.UnixTimestampToFormat(t, "15:04:05") + `</small></div>`
  395. },
  396. },
  397. {
  398. DBField: "active",
  399. NameInTable: "Active",
  400. Classes: "d-none d-sm-table-cell",
  401. CallBack: func(values *[]string) string {
  402. return builder.CheckBox(utils.StrToInt((*values)[6]))
  403. },
  404. },
  405. },
  406. func(values *[]string) string {
  407. return builder.DataTableAction(&[]builder.DataTableActionRow{
  408. {
  409. Icon: assets.SysSvgIconView,
  410. Href: `/shop/` + (*values)[2] + `/`,
  411. Hint: "View",
  412. Target: "_blank",
  413. },
  414. {
  415. Icon: assets.SysSvgIconEdit,
  416. Href: "/cp/" + wrap.CurrModule + "/modify/" + (*values)[0] + "/",
  417. Hint: "Edit",
  418. },
  419. {
  420. Icon: assets.SysSvgIconRemove,
  421. Href: "javascript:fave.ActionDataTableDelete(this,'shop-delete','" +
  422. (*values)[0] + "','Are you sure want to delete product?');",
  423. Hint: "Delete",
  424. Classes: "delete",
  425. },
  426. })
  427. },
  428. "/cp/"+wrap.CurrModule+"/",
  429. nil,
  430. nil,
  431. true,
  432. )
  433. } else if wrap.CurrSubModule == "categories" {
  434. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  435. {Name: "Categories", Link: "/cp/" + wrap.CurrModule + "/" + wrap.CurrSubModule + "/"},
  436. {Name: "List of categories"},
  437. })
  438. content += builder.DataTable(
  439. wrap,
  440. "shop_cats",
  441. "id",
  442. "ASC",
  443. &[]builder.DataTableRow{
  444. {
  445. DBField: "id",
  446. },
  447. {
  448. DBField: "user",
  449. },
  450. {
  451. DBField: "name",
  452. NameInTable: "Category",
  453. CallBack: func(values *[]string) string {
  454. depth := utils.StrToInt((*values)[4]) - 1
  455. if depth < 0 {
  456. depth = 0
  457. }
  458. sub := strings.Repeat("&mdash; ", depth)
  459. name := `<a href="/cp/` + wrap.CurrModule + `/categories-modify/` + (*values)[0] + `/">` + sub + html.EscapeString((*values)[2]) + `</a>`
  460. return `<div>` + name + `</div>`
  461. },
  462. },
  463. {
  464. DBField: "alias",
  465. },
  466. {
  467. DBField: "depth",
  468. },
  469. },
  470. func(values *[]string) string {
  471. return builder.DataTableAction(&[]builder.DataTableActionRow{
  472. {
  473. Icon: assets.SysSvgIconView,
  474. Href: `/shop/category/` + (*values)[3] + `/`,
  475. Hint: "View",
  476. Target: "_blank",
  477. },
  478. {
  479. Icon: assets.SysSvgIconEdit,
  480. Href: "/cp/" + wrap.CurrModule + "/categories-modify/" + (*values)[0] + "/",
  481. Hint: "Edit",
  482. },
  483. {
  484. Icon: assets.SysSvgIconRemove,
  485. Href: "javascript:fave.ActionDataTableDelete(this,'shop-categories-delete','" +
  486. (*values)[0] + "','Are you sure want to delete category?');",
  487. Hint: "Delete",
  488. Classes: "delete",
  489. },
  490. })
  491. },
  492. "/cp/"+wrap.CurrModule+"/"+wrap.CurrSubModule+"/",
  493. nil,
  494. func(limit_offset int, pear_page int) (*sqlw.Rows, error) {
  495. return wrap.DB.Query(
  496. `SELECT
  497. node.id,
  498. node.user,
  499. node.name,
  500. node.alias,
  501. (COUNT(parent.id) - 1) AS depth
  502. FROM
  503. shop_cats AS node,
  504. shop_cats AS parent
  505. WHERE
  506. node.lft BETWEEN parent.lft AND parent.rgt AND
  507. node.id > 1
  508. GROUP BY
  509. node.id
  510. ORDER BY
  511. node.lft ASC
  512. ;`,
  513. )
  514. },
  515. false,
  516. )
  517. } else if wrap.CurrSubModule == "attributes" {
  518. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  519. {Name: "Attributes", Link: "/cp/" + wrap.CurrModule + "/" + wrap.CurrSubModule + "/"},
  520. {Name: "List of attributes"},
  521. })
  522. content += builder.DataTable(
  523. wrap,
  524. "shop_filters",
  525. "id",
  526. "DESC",
  527. &[]builder.DataTableRow{
  528. {
  529. DBField: "id",
  530. },
  531. {
  532. DBField: "name",
  533. NameInTable: "Name",
  534. CallBack: func(values *[]string) string {
  535. name := `<a href="/cp/` + wrap.CurrModule + `/attributes-modify/` + (*values)[0] + `/">` + html.EscapeString((*values)[1]) + `</a>`
  536. return `<div>` + name + `</div><div><small>` + html.EscapeString((*values)[2]) + `</small></div>`
  537. },
  538. },
  539. {
  540. DBField: "filter",
  541. },
  542. },
  543. func(values *[]string) string {
  544. return builder.DataTableAction(&[]builder.DataTableActionRow{
  545. {
  546. Icon: assets.SysSvgIconEdit,
  547. Href: "/cp/" + wrap.CurrModule + "/attributes-modify/" + (*values)[0] + "/",
  548. Hint: "Edit",
  549. },
  550. {
  551. Icon: assets.SysSvgIconRemove,
  552. Href: "javascript:fave.ActionDataTableDelete(this,'shop-attributes-delete','" +
  553. (*values)[0] + "','Are you sure want to delete attribute?');",
  554. Hint: "Delete",
  555. Classes: "delete",
  556. },
  557. })
  558. },
  559. "/cp/"+wrap.CurrModule+"/",
  560. nil,
  561. nil,
  562. true,
  563. )
  564. } else if wrap.CurrSubModule == "currencies" {
  565. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  566. {Name: "Currencies", Link: "/cp/" + wrap.CurrModule + "/" + wrap.CurrSubModule + "/"},
  567. {Name: "List of currencies"},
  568. })
  569. content += builder.DataTable(
  570. wrap,
  571. "shop_currencies",
  572. "id",
  573. "DESC",
  574. &[]builder.DataTableRow{
  575. {
  576. DBField: "id",
  577. },
  578. {
  579. DBField: "name",
  580. NameInTable: "Name",
  581. CallBack: func(values *[]string) string {
  582. name := `<a href="/cp/` + wrap.CurrModule + `/currencies-modify/` + (*values)[0] + `/">` + html.EscapeString((*values)[1]) + ` (` + (*values)[3] + `, ` + (*values)[4] + `)</a>`
  583. return `<div>` + name + `</div>`
  584. },
  585. },
  586. {
  587. DBField: "coefficient",
  588. NameInTable: "Coefficient",
  589. Classes: "d-none d-md-table-cell",
  590. CallBack: func(values *[]string) string {
  591. return utils.Float64ToStrF(utils.StrToFloat64((*values)[2]), "%.4f")
  592. },
  593. },
  594. {
  595. DBField: "code",
  596. },
  597. {
  598. DBField: "symbol",
  599. },
  600. },
  601. func(values *[]string) string {
  602. return builder.DataTableAction(&[]builder.DataTableActionRow{
  603. {
  604. Icon: assets.SysSvgIconEdit,
  605. Href: "/cp/" + wrap.CurrModule + "/currencies-modify/" + (*values)[0] + "/",
  606. Hint: "Edit",
  607. },
  608. {
  609. Icon: assets.SysSvgIconRemove,
  610. Href: "javascript:fave.ActionDataTableDelete(this,'shop-currencies-delete','" +
  611. (*values)[0] + "','Are you sure want to delete currency?');",
  612. Hint: "Delete",
  613. Classes: "delete",
  614. },
  615. })
  616. },
  617. "/cp/"+wrap.CurrModule+"/",
  618. nil,
  619. nil,
  620. true,
  621. )
  622. } else if wrap.CurrSubModule == "add" || wrap.CurrSubModule == "modify" {
  623. if wrap.CurrSubModule == "add" {
  624. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  625. {Name: "Add new product"},
  626. })
  627. } else {
  628. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  629. {Name: "Modify product"},
  630. })
  631. }
  632. data := utils.MySql_shop_product{
  633. A_id: 0,
  634. A_user: 0,
  635. A_name: "",
  636. A_alias: "",
  637. A_content: "",
  638. A_datetime: 0,
  639. A_active: 0,
  640. }
  641. if wrap.CurrSubModule == "modify" {
  642. if len(wrap.UrlArgs) != 3 {
  643. return "", "", ""
  644. }
  645. if !utils.IsNumeric(wrap.UrlArgs[2]) {
  646. return "", "", ""
  647. }
  648. err := wrap.DB.QueryRow(`
  649. SELECT
  650. id,
  651. user,
  652. currency,
  653. price,
  654. name,
  655. alias,
  656. briefly,
  657. content,
  658. active
  659. FROM
  660. shop_products
  661. WHERE
  662. id = ?
  663. LIMIT 1;`,
  664. utils.StrToInt(wrap.UrlArgs[2]),
  665. ).Scan(
  666. &data.A_id,
  667. &data.A_user,
  668. &data.A_currency,
  669. &data.A_price,
  670. &data.A_name,
  671. &data.A_alias,
  672. &data.A_briefly,
  673. &data.A_content,
  674. &data.A_active,
  675. )
  676. if err != nil {
  677. return "", "", ""
  678. }
  679. }
  680. // All product current categories
  681. var selids []int
  682. if data.A_id > 0 {
  683. rows, err := wrap.DB.Query("SELECT category_id FROM shop_cat_product_rel WHERE product_id = ?;", data.A_id)
  684. if err == nil {
  685. defer rows.Close()
  686. values := make([]int, 1)
  687. scan := make([]interface{}, len(values))
  688. for i := range values {
  689. scan[i] = &values[i]
  690. }
  691. for rows.Next() {
  692. err = rows.Scan(scan...)
  693. if err == nil {
  694. selids = append(selids, int(values[0]))
  695. }
  696. }
  697. }
  698. }
  699. btn_caption := "Add"
  700. if wrap.CurrSubModule == "modify" {
  701. btn_caption = "Save"
  702. }
  703. content += builder.DataForm(wrap, []builder.DataFormField{
  704. {
  705. Kind: builder.DFKHidden,
  706. Name: "action",
  707. Value: "shop-modify",
  708. },
  709. {
  710. Kind: builder.DFKHidden,
  711. Name: "id",
  712. Value: utils.IntToStr(data.A_id),
  713. },
  714. {
  715. Kind: builder.DFKText,
  716. Caption: "Product name",
  717. Name: "name",
  718. Value: data.A_name,
  719. Required: true,
  720. Min: "1",
  721. Max: "255",
  722. },
  723. {
  724. Kind: builder.DFKText,
  725. Caption: "Product price",
  726. Name: "price",
  727. Value: "0",
  728. CallBack: func(field *builder.DataFormField) string {
  729. return `<div class="form-group n3">` +
  730. `<div class="row">` +
  731. `<div class="col-md-3">` +
  732. `<label for="lbl_price">Product price</label>` +
  733. `</div>` +
  734. `<div class="col-md-9">` +
  735. `<div>` +
  736. `<div class="row">` +
  737. `<div class="col-md-8">` +
  738. `<div><input class="form-control" type="number" step="0.01" id="lbl_price" name="price" value="` + utils.Float64ToStr(data.A_price) + `" placeholder="" autocomplete="off" required></div>` +
  739. `<div class="d-md-none mb-3"></div>` +
  740. `</div>` +
  741. `<div class="col-md-4">` +
  742. `<select class="selectpicker form-control" id="lbl_currency" name="currency" data-live-search="true">` +
  743. this.shop_GetCurrencySelectOptions(wrap, data.A_currency) +
  744. `</select>` +
  745. `</div>` +
  746. `</div>` +
  747. `</div>` +
  748. `</div>` +
  749. `</div>` +
  750. `</div>`
  751. },
  752. },
  753. {
  754. Kind: builder.DFKText,
  755. Caption: "Product alias",
  756. Name: "alias",
  757. Value: data.A_alias,
  758. Hint: "Example: mobile-phone",
  759. Max: "255",
  760. },
  761. {
  762. Kind: builder.DFKText,
  763. Caption: "Categories",
  764. Name: "cats",
  765. Value: "0",
  766. CallBack: func(field *builder.DataFormField) string {
  767. return `<div class="form-group n5">` +
  768. `<div class="row">` +
  769. `<div class="col-md-3">` +
  770. `<label for="lbl_parent">Categories</label>` +
  771. `</div>` +
  772. `<div class="col-md-9">` +
  773. `<div>` +
  774. `<select class="selectpicker form-control" id="lbl_cats" name="cats[]" data-live-search="true" multiple>` +
  775. this.shop_GetCategorySelectOptions(wrap, 0, 0, selids) +
  776. `</select>` +
  777. `</div>` +
  778. `</div>` +
  779. `</div>` +
  780. `</div>`
  781. },
  782. },
  783. {
  784. Kind: builder.DFKText,
  785. Caption: "Attributes",
  786. Name: "",
  787. Value: "",
  788. CallBack: func(field *builder.DataFormField) string {
  789. return `<div class="form-group n6">` +
  790. `<div class="row">` +
  791. `<div class="col-md-3">` +
  792. `<label>Attributes</label>` +
  793. `</div>` +
  794. `<div class="col-md-9">` +
  795. `<div class="list-wrapper">` +
  796. `<div id="list">` +
  797. this.shop_GetProductValuesInputs(wrap, data.A_id) +
  798. `</div>` +
  799. `<div class="list-button position-relative">` +
  800. `<select class="selectpicker form-control" id="lbl_attributes" data-live-search="true">` +
  801. this.shop_GetAllAttributesSelectOptions(wrap) +
  802. `</select>` +
  803. `<button type="button" class="btn btn-success btn-dynamic-remove" onclick="fave.ShopProductsAdd();">Add attribute</button>` +
  804. `</div>` +
  805. `</div>` +
  806. `</div>` +
  807. `</div>` +
  808. `</div>`
  809. },
  810. },
  811. {
  812. Kind: builder.DFKTextArea,
  813. Caption: "Briefly",
  814. Name: "briefly",
  815. Value: data.A_briefly,
  816. Classes: "briefly wysiwyg",
  817. },
  818. {
  819. Kind: builder.DFKTextArea,
  820. Caption: "Product content",
  821. Name: "content",
  822. Value: data.A_content,
  823. Classes: "wysiwyg",
  824. },
  825. {
  826. Kind: builder.DFKText,
  827. Caption: "Product images",
  828. Name: "",
  829. Value: "",
  830. CallBack: func(field *builder.DataFormField) string {
  831. if data.A_id == 0 {
  832. return ``
  833. }
  834. return `<div class="form-group n6">` +
  835. `<div class="row">` +
  836. `<div class="col-md-3">` +
  837. `<label>Product images</label>` +
  838. `</div>` +
  839. `<div class="col-md-9">` +
  840. `<div class="list-wrapper">` +
  841. //
  842. `<div id="list">` +
  843. `` +
  844. `</div>` +
  845. `<div class="list-button position-relative">` +
  846. `<input class="form-control" type="file" id="file" name="file" /><button type="button" class="btn btn-success btn-dynamic-remove" onclick="fave.ShopProductsUploadImage('shop-upload-image', ` + utils.IntToStr(data.A_id) + `, 'file');">Upload</button>` +
  847. `</div>` +
  848. //
  849. `</div>` +
  850. `</div>` +
  851. `</div>` +
  852. `</div>`
  853. },
  854. },
  855. {
  856. Kind: builder.DFKCheckBox,
  857. Caption: "Active",
  858. Name: "active",
  859. Value: utils.IntToStr(data.A_active),
  860. },
  861. {
  862. Kind: builder.DFKSubmit,
  863. Value: btn_caption,
  864. Target: "add-edit-button",
  865. },
  866. })
  867. if wrap.CurrSubModule == "add" {
  868. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Add</button>`
  869. } else {
  870. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Save</button>`
  871. }
  872. } else if wrap.CurrSubModule == "categories-add" || wrap.CurrSubModule == "categories-modify" {
  873. if wrap.CurrSubModule == "categories-add" {
  874. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  875. {Name: "Categories", Link: "/cp/" + wrap.CurrModule + "/categories/"},
  876. {Name: "Add new category"},
  877. })
  878. } else {
  879. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  880. {Name: "Categories", Link: "/cp/" + wrap.CurrModule + "/categories/"},
  881. {Name: "Modify category"},
  882. })
  883. }
  884. data := utils.MySql_shop_category{
  885. A_id: 0,
  886. A_user: 0,
  887. A_name: "",
  888. A_alias: "",
  889. A_lft: 0,
  890. A_rgt: 0,
  891. }
  892. if wrap.CurrSubModule == "categories-modify" {
  893. if len(wrap.UrlArgs) != 3 {
  894. return "", "", ""
  895. }
  896. if !utils.IsNumeric(wrap.UrlArgs[2]) {
  897. return "", "", ""
  898. }
  899. err := wrap.DB.QueryRow(`
  900. SELECT
  901. id,
  902. user,
  903. name,
  904. alias,
  905. lft,
  906. rgt
  907. FROM
  908. shop_cats
  909. WHERE
  910. id = ?
  911. LIMIT 1;`,
  912. utils.StrToInt(wrap.UrlArgs[2]),
  913. ).Scan(
  914. &data.A_id,
  915. &data.A_user,
  916. &data.A_name,
  917. &data.A_alias,
  918. &data.A_lft,
  919. &data.A_rgt,
  920. )
  921. if err != nil {
  922. return "", "", ""
  923. }
  924. }
  925. btn_caption := "Add"
  926. if wrap.CurrSubModule == "categories-modify" {
  927. btn_caption = "Save"
  928. }
  929. parentId := 0
  930. if wrap.CurrSubModule == "categories-modify" {
  931. parentId = this.shop_GetCategoryParentId(wrap, data.A_id)
  932. }
  933. content += builder.DataForm(wrap, []builder.DataFormField{
  934. {
  935. Kind: builder.DFKHidden,
  936. Name: "action",
  937. Value: "shop-categories-modify",
  938. },
  939. {
  940. Kind: builder.DFKHidden,
  941. Name: "id",
  942. Value: utils.IntToStr(data.A_id),
  943. },
  944. {
  945. Kind: builder.DFKText,
  946. Caption: "Parent",
  947. Name: "parent",
  948. Value: "0",
  949. CallBack: func(field *builder.DataFormField) string {
  950. return `<div class="form-group n2">` +
  951. `<div class="row">` +
  952. `<div class="col-md-3">` +
  953. `<label for="lbl_parent">Parent</label>` +
  954. `</div>` +
  955. `<div class="col-md-9">` +
  956. `<div>` +
  957. `<select class="selectpicker form-control" id="lbl_parent" name="parent" data-live-search="true">` +
  958. `<option title="Nothing selected" value="0">&mdash;</option>` +
  959. this.shop_GetCategorySelectOptions(wrap, data.A_id, parentId, []int{}) +
  960. `</select>` +
  961. `</div>` +
  962. `</div>` +
  963. `</div>` +
  964. `</div>`
  965. },
  966. },
  967. {
  968. Kind: builder.DFKText,
  969. Caption: "Name",
  970. Name: "name",
  971. Value: data.A_name,
  972. Required: true,
  973. Min: "1",
  974. Max: "255",
  975. },
  976. {
  977. Kind: builder.DFKText,
  978. Caption: "Alias",
  979. Name: "alias",
  980. Value: data.A_alias,
  981. Hint: "Example: popular-products",
  982. Max: "255",
  983. },
  984. {
  985. Kind: builder.DFKSubmit,
  986. Value: btn_caption,
  987. Target: "add-edit-button",
  988. },
  989. })
  990. if wrap.CurrSubModule == "categories-add" {
  991. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Add</button>`
  992. } else {
  993. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Save</button>`
  994. }
  995. } else if wrap.CurrSubModule == "attributes-add" || wrap.CurrSubModule == "attributes-modify" {
  996. if wrap.CurrSubModule == "attributes-add" {
  997. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  998. {Name: "Attributes", Link: "/cp/" + wrap.CurrModule + "/attributes/"},
  999. {Name: "Add new attribute"},
  1000. })
  1001. } else {
  1002. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  1003. {Name: "Attributes", Link: "/cp/" + wrap.CurrModule + "/attributes/"},
  1004. {Name: "Modify attribute"},
  1005. })
  1006. }
  1007. data := utils.MySql_shop_filter{
  1008. A_id: 0,
  1009. A_name: "",
  1010. A_filter: "",
  1011. }
  1012. if wrap.CurrSubModule == "attributes-modify" {
  1013. if len(wrap.UrlArgs) != 3 {
  1014. return "", "", ""
  1015. }
  1016. if !utils.IsNumeric(wrap.UrlArgs[2]) {
  1017. return "", "", ""
  1018. }
  1019. err := wrap.DB.QueryRow(`
  1020. SELECT
  1021. id,
  1022. name,
  1023. filter
  1024. FROM
  1025. shop_filters
  1026. WHERE
  1027. id = ?
  1028. LIMIT 1;`,
  1029. utils.StrToInt(wrap.UrlArgs[2]),
  1030. ).Scan(
  1031. &data.A_id,
  1032. &data.A_name,
  1033. &data.A_filter,
  1034. )
  1035. if err != nil {
  1036. return "", "", ""
  1037. }
  1038. }
  1039. btn_caption := "Add"
  1040. if wrap.CurrSubModule == "attributes-modify" {
  1041. btn_caption = "Save"
  1042. }
  1043. content += builder.DataForm(wrap, []builder.DataFormField{
  1044. {
  1045. Kind: builder.DFKHidden,
  1046. Name: "action",
  1047. Value: "shop-attributes-modify",
  1048. },
  1049. {
  1050. Kind: builder.DFKHidden,
  1051. Name: "id",
  1052. Value: utils.IntToStr(data.A_id),
  1053. },
  1054. {
  1055. Kind: builder.DFKText,
  1056. Caption: "Attribute name",
  1057. Name: "name",
  1058. Value: data.A_name,
  1059. Required: true,
  1060. Min: "1",
  1061. Max: "255",
  1062. },
  1063. {
  1064. Kind: builder.DFKText,
  1065. Caption: "Attribute in filter",
  1066. Name: "filter",
  1067. Value: data.A_filter,
  1068. Required: true,
  1069. Min: "1",
  1070. Max: "255",
  1071. },
  1072. {
  1073. Kind: builder.DFKText,
  1074. Caption: "Attribute values",
  1075. Name: "",
  1076. Value: "",
  1077. CallBack: func(field *builder.DataFormField) string {
  1078. return `<div class="form-group n4">` +
  1079. `<div class="row">` +
  1080. `<div class="col-md-3">` +
  1081. `<label>Attribute values</label>` +
  1082. `</div>` +
  1083. `<div class="col-md-9">` +
  1084. `<div class="list-wrapper">` +
  1085. `<div id="list">` +
  1086. this.shop_GetFilterValuesInputs(wrap, data.A_id) +
  1087. `</div>` +
  1088. `<div class="list-button"><button type="button" class="btn btn-success" onclick="fave.ShopAttributesAdd();">Add attribute value</button></div>` +
  1089. `</div>` +
  1090. `</div>` +
  1091. `</div>` +
  1092. `</div>`
  1093. },
  1094. },
  1095. {
  1096. Kind: builder.DFKSubmit,
  1097. Value: btn_caption,
  1098. Target: "add-edit-button",
  1099. },
  1100. })
  1101. if wrap.CurrSubModule == "attributes-add" {
  1102. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Add</button>`
  1103. } else {
  1104. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Save</button>`
  1105. }
  1106. } else if wrap.CurrSubModule == "currencies-add" || wrap.CurrSubModule == "currencies-modify" {
  1107. if wrap.CurrSubModule == "currencies-add" {
  1108. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  1109. {Name: "Currencies", Link: "/cp/" + wrap.CurrModule + "/currencies/"},
  1110. {Name: "Add new currency"},
  1111. })
  1112. } else {
  1113. content += this.getBreadCrumbs(wrap, &[]consts.BreadCrumb{
  1114. {Name: "Currencies", Link: "/cp/" + wrap.CurrModule + "/currencies/"},
  1115. {Name: "Modify currency"},
  1116. })
  1117. }
  1118. data := utils.MySql_shop_currency{
  1119. A_id: 0,
  1120. A_name: "",
  1121. A_coefficient: 0,
  1122. A_code: "",
  1123. A_symbol: "",
  1124. }
  1125. if wrap.CurrSubModule == "currencies-modify" {
  1126. if len(wrap.UrlArgs) != 3 {
  1127. return "", "", ""
  1128. }
  1129. if !utils.IsNumeric(wrap.UrlArgs[2]) {
  1130. return "", "", ""
  1131. }
  1132. err := wrap.DB.QueryRow(`
  1133. SELECT
  1134. id,
  1135. name,
  1136. coefficient,
  1137. code,
  1138. symbol
  1139. FROM
  1140. shop_currencies
  1141. WHERE
  1142. id = ?
  1143. LIMIT 1;`,
  1144. utils.StrToInt(wrap.UrlArgs[2]),
  1145. ).Scan(
  1146. &data.A_id,
  1147. &data.A_name,
  1148. &data.A_coefficient,
  1149. &data.A_code,
  1150. &data.A_symbol,
  1151. )
  1152. if err != nil {
  1153. return "", "", ""
  1154. }
  1155. }
  1156. btn_caption := "Add"
  1157. if wrap.CurrSubModule == "currencies-modify" {
  1158. btn_caption = "Save"
  1159. }
  1160. content += builder.DataForm(wrap, []builder.DataFormField{
  1161. {
  1162. Kind: builder.DFKHidden,
  1163. Name: "action",
  1164. Value: "shop-currencies-modify",
  1165. },
  1166. {
  1167. Kind: builder.DFKHidden,
  1168. Name: "id",
  1169. Value: utils.IntToStr(data.A_id),
  1170. },
  1171. {
  1172. Kind: builder.DFKText,
  1173. Caption: "Currency name",
  1174. Name: "name",
  1175. Value: data.A_name,
  1176. Required: true,
  1177. Min: "1",
  1178. Max: "255",
  1179. },
  1180. {
  1181. Kind: builder.DFKText,
  1182. Caption: "Currency coefficient",
  1183. Name: "coefficient",
  1184. Value: "0",
  1185. CallBack: func(field *builder.DataFormField) string {
  1186. return `<div class="form-group n3">` +
  1187. `<div class="row">` +
  1188. `<div class="col-md-3">` +
  1189. `<label for="lbl_coefficient">Currency coefficient</label>` +
  1190. `</div>` +
  1191. `<div class="col-md-9">` +
  1192. `<div><input class="form-control" type="number" step="0.0001" id="lbl_coefficient" name="coefficient" value="` + utils.Float64ToStrF(data.A_coefficient, "%.4f") + `" placeholder="" autocomplete="off" required></div>` +
  1193. `</div>` +
  1194. `</div>` +
  1195. `</div>`
  1196. },
  1197. },
  1198. {
  1199. Kind: builder.DFKText,
  1200. Caption: "Currency code",
  1201. Name: "code",
  1202. Value: data.A_code,
  1203. Required: true,
  1204. Min: "1",
  1205. Max: "10",
  1206. },
  1207. {
  1208. Kind: builder.DFKText,
  1209. Caption: "Currency symbol",
  1210. Name: "symbol",
  1211. Value: data.A_symbol,
  1212. Required: true,
  1213. Min: "1",
  1214. Max: "5",
  1215. },
  1216. {
  1217. Kind: builder.DFKSubmit,
  1218. Value: btn_caption,
  1219. Target: "add-edit-button",
  1220. },
  1221. })
  1222. if wrap.CurrSubModule == "currencies-add" {
  1223. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Add</button>`
  1224. } else {
  1225. sidebar += `<button class="btn btn-primary btn-sidebar" id="add-edit-button">Save</button>`
  1226. }
  1227. }
  1228. return this.getSidebarModules(wrap), content, sidebar
  1229. })
  1230. }