diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | catalog.cfg | 750 | ||||
-rw-r--r-- | html/css/product.css | 74 | ||||
-rw-r--r-- | namatoko.scss | 6 | ||||
-rw-r--r-- | pages/flypage.html | 77 | ||||
-rw-r--r-- | pages/ord/basket.html | 158 | ||||
-rw-r--r-- | pages/ord/checkout.html | 138 | ||||
-rw-r--r-- | pages/results.html | 92 | ||||
-rw-r--r-- | templates/layout/noleft | 52 | ||||
-rw-r--r-- | variables/BOTTOM | 16 |
10 files changed, 1366 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0bea6b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*~ +*.min.css +*.swp diff --git a/catalog.cfg b/catalog.cfg new file mode 100644 index 0000000..45ad3be --- /dev/null +++ b/catalog.cfg @@ -0,0 +1,750 @@ +#==========================================================================# + +# catalog.cfg + +#==========================================================================# + +## We want to hide the .gdbm, .autonumber, .numeric, .sql, .db files +## Needs to go before VariableDatabase +DatabaseDefault HIDE_AUTO_FILES 1 + +# Read in some initial default variables from a directory. These are +# larger ones -- they could conceivably be maintained in the +# VariableDatabase but might be easier to maintain in a file, especially +# for development. +# +# Notable settings: COMMON_MENU, COPYRIGHT +# +DirConfig Variable variables + +# Read in large variables or dynamic variables from a series +# of files in a directory +VariableDatabase variable + +# Read in site-specific variables from a database (if any). +# Used to create a information for a temporarily transferred +# catalog, i.e. for development. +# +# Ignored if file doesn't exist. +VariableDatabase site + +# Warn if any important modules or usertags are missing. + +Require module Digest::MD5 "Need %s %s for better cache keys." +Require module Safe::Hole "Need %s %s for embedded perl object access." + +# Encoding +Variable MV_UTF8 1 +Variable MV_HTTP_CHARSET utf-8 + +#==========================================================================# + +# Can send various files outside the catalog directory, for disk partition +# reasons, for example. + +ParseVariables Yes + +ifdef LOGDIR +ErrorFile __LOGDIR__/error.log +AsciiTrack __LOGDIR__/tracking.asc +#TrackFile __LOGDIR__/usertrack +endif + +ifndef LOGDIR +AsciiTrack logs/tracking.asc +#TrackFile logs/usertrack +endif + +ifdef CACHEDIR +SessionDatabase __CACHEDIR__/session +ScratchDir __CACHEDIR__/tmp +endif + +ifndef CACHEDIR +SessionDatabase session +ScratchDir tmp +endif + +ifdef RUNDIR +RunDir __RUNDIR__ +endif + +#==========================================================================# + +# Database setup + +ParseVariables Yes + +ifndef INDEV +DatabaseDefault LOG_ERROR_SESSION 0 +endif + +## Quote all identifiers to avoid name clash issues and SQL injection attacks +DatabaseDefault QUOTE_IDENTIFIERS 1 + +ifdef MYSQL +DatabaseDefault NO_ASCII_INDEX 1 +include dbconf/mysql/mysql.cfg +endif + +ifdef PGSQL +DatabaseDefault NO_ASCII_INDEX 1 +include dbconf/pgsql/pgsql.cfg +endif + +ifdef SQLITE +DatabaseDefault NO_ASCII_INDEX 1 +include dbconf/sqlite/sqlite.cfg +endif + +# Default DBM if nothing else defined +ifndef SOME_DATABASE +Require We_need_a_database "%s, must have either MySQL or Postgres" +endif + +### Uncomment these for DBI sessions +# SessionType DBI +# SessionDB sessions +# Database sessions sessions.txt __SQLDSN__ +# NoImport sessions + +### You can change the length of the session ID. Must be integer between +### 8 and 32. +# Limit session_id_length 8 + +#==========================================================================# + +# The URLs which are written to refer back to our catalog. + +<ParseVariables Yes> + +VendURL http://__SERVER_NAME____CGI_URL__ +SecureURL __SECURE_SERVER____CGI_URL__ + +ifndef SECURE_ENABLE +SecureURL http://__SERVER_NAME____CGI_URL__ +endif + +# Set the image path for relative images +ImageDir __IMAGE_DIR__/ +ImageDirInternal http://__SERVER_NAME____IMAGE_DIR__/ + +</ParseVariables> + +#==========================================================================# + +# Here we set up the catalog theme. + +<ParseVariables Yes> + +## +## Here we pick up the region variables like LEFTONLY_TOP and LEFTONLY_BOTTOM. +## +## Commenting; new variables TOP and BOTTOM will be defined in variable directory. +## No longer going to need multiple leftright_top, leftonly_top, etc; will be +## defined in "display_class" in each page. + +#DirConfig Variable templates/regions + +# TRAFFIC is defined system-wide in interchange.cfg. When in low-traffic +# mode, variables are dynamic -- i.e. when you change the region file +# the page changes. Otherwise a reconfig must be done to change the +# variable. +# +# Careful, this can change the way *other* variables are interpreted. +ifdef @TRAFFIC =~ /low/i +Pragma dynamic_variables +Pragma dynamic_variables_file_only +endif + +# Provide some links for the admin to edit pages if we have the UI in +ifdef @UI +Autoload admin_links +endif + +#==========================================================================# + +# Internationalization +LocaleDatabase locale + +# Default locale +ifdef DEFAULT_LOCALE +DefaultLocale __DEFAULT_LOCALE__ +endif + +ifdef DEFAULT_LOCALE +# Database definitions for localized databases +include dbconf/locales/{__LOCALES__}.cfg +endif + +Locale en_US currency_symbol Rp +Locale en_US mon_decimal_point "" +Locale en_US frac_digits 0 + +Locale id_ID currency_symbol Rp +Locale id_ID p_cs_precedes 1 +Locale id_ID mon_thousands_sep . +Locale id_ID mon_decimal_point "" +Locale id_ID frac_digits 0 + +#==========================================================================# + +# Various catalog settings. + +ScratchDefault mv_add_dot_html 0 +ScratchDefault mv_no_session_id 1 +ScratchDefault mv_no_count 1 + +ifdef DEFAULT_COUNTRY +ValuesDefault country __DEFAULT_COUNTRY__ +ValuesDefault b_country __DEFAULT_COUNTRY__ +endif + +ValuesDefault fname Pengguna lname Baru + +DirectoryIndex index.html +SpecialPage catalog index +SpecialPage violation ../special_pages/violation +SpecialPage put_handler admin_publish +SpecialPage report ../etc/report +SpecialPage receipt ../etc/receipt + +# Allow others in our group to read/write files by default +ReadPermission group +WritePermission group + +# Don't send X-Track HTTP response header +UserTrack no + +# If a specific user session accesses this catalog more than this many +# times with no pauses of 30 seconds or more, the LockoutCommand (if set) +# is executed. Set this to 0 if you're getting links to 127.0.0.1 +# during your testing. +RobotLimit 100 + +# We also count the number of accesses from the same IP address. More than +# RobotLimit accesses within a one hour period will cause a Forbidden result. +# Uncomment the following line to change the 1 hour period to 30 minutes. +# Limit ip_session_expire 30 + +# The default lockout time is one day. Uncomment the following line to change +# it to just under an hour. +# Limit robot_expire 0.04 + +# Strip whitespace from top of pages so you don't have to scroll down +# before the HTML starts when doing 'view source' in a browser. +Pragma strip_white + +# Don't allow <!--[itl tag]--> +Pragma no_html_comment_embed + +## DATA INSERTION PRAGMAS for form database inserts or updates + +## Restricts inserts to insert-only, but allows the fall-through +## behavior from update to insert. No existing data will be clobbered. +# Pragma dml=preserve + +## Pragma 'dml=strict' forces update or insert to only perform the +## requested action. +# Pragma dml=strict + +## Pragma 'dml=upsert' (the default) preserves Interchange's original +## method of doing an update if the record exists, otherwise doing an +## insert. This can clobber an existing key. +# Pragma dml=upsert + +## END DATA INSERTION PRAGMAS + +#==========================================================================# + +# User session related settings. + +# Whether to encrypt passwords in UserDB +UserDB default crypt 1 +UserDB default bcrypt 1 + +# These 2 lines are needed for query/pw_reset +UserDB default promote 1 +UserDB default from_plain 1 + +# The pepper should be unique for your site, but note that if you change this, +# it will make previously-crypted passwords inaccessible -- +# so set this before you start adding users, or don't set at all. +UserDB default bcrypt_pepper __BCRYPT_PEPPER__ + +# Set to 1 to make the username and password case-insensitive +UserDB default ignore_case 1 + +# Change a field to something that doesn't conflict in MySQL +UserDB default time_field mod_time + +# Don't want people setting their credit limit or dealer status directly +UserDB default scratch "dealer price_level credit_limit usernick" +UserDB default expire_field expiration + +# Update the database with the date when creating a user +UserDB default created_date_iso created + +# Update the database with the date when updating a user via set_values +UserDB default updated_date_iso updated + +# Set some other things you want to retain but which don't deserve their +# own field -- stored in serial field preferences +UserDB default extra_fields <<EOU +" + mv_same_billing + mv_credit_card_exp_type + mv_credit_card_exp_month + mv_credit_card_exp_year + fax_order + phone_cell + account_id + po_number +" +EOU + +# Log user login/logout etc in separate logfile +UserDB default logfile logs/userdb.log + +# This makes the login username the user's email address +# Uncomment to use +UserDB default indirect_login usernick +UserDB default assign_username 1 + +# for indirect login to work with admin +UserDB autocreate crypt 0 +UserDB autocreate time_field mod_time +UserDB autocreate scratch "dealer price_level credit_limit" + +# This makes the password be inserted in an insert-only table. +# +# UserDB default enclair_db enclair +# +# You can set the following, which have the defaults shown in the +# setting. You can also insert %M, which is the MD5 of the password, or +# %D which is a datetime localtime value in the form YYYYmmddHHMMSS. +#UserDB default enclair_key_field username +#UserDB default enclair_field password +#UserDB default enclair_query_template "INSERT INTO %t (%U,%P) values (%u,%p)" + +# minimal login stuff for affiliate +UserDB affiliate user_field affiliate +UserDB affiliate database affiliate +UserDB affiliate time_field none +UserDB affiliate crypt 0 + +# Set this to Yes if you want auto-login capability for users. +# You must be careful about malicious JavaScripts in your embedded code. +## this is not secure at all, since it saves user's password. +## can use alternate cookie to just save username. +CookieLogin No + +# Limit the time your customers can retain a session +SessionExpire 4 hours + +# Limit the time your customers can CookieLogin +SaveExpire 30 days + +#==========================================================================# + +# Cart, order, and route settings. + +ParseVariables Yes + +MailOrderTo __ORDERS_TO__ + +AlwaysSecure <<EOD + * +EOD + +## Set this if you have a different secure server +#AlwaysSecure order ord/basket process + +ifndef COMMON_ORDER_PROFILE +Variable COMMON_ORDER_PROFILE <<EOV + [calc] + if ($CGI->{state_cs_in} && !$CGI->{state}){ + $CGI->{state} = $CGI->{state_cs_in}; + } + return; + [/calc] + mv_same_billing=always_pass + fname=required + lname=required + address1=required + address2=always_pass + city=required + country=required + state=multistate + zip=multizip + phone_day=phone + [if !session logged_in]email=email_only[/if] +EOV +endif + +## Payment-related stuff + +EncryptKey __PGP_KEY__ + +## These routes are not order routes, but payment routes +Route authorizenet id "__MV_PAYMENT_ID__" +Route authorizenet secret "__MV_PAYMENT_SECRET__" +Route authorizenet host "__MV_PAYMENT_HOST__" +Route authorizenet referer "__MV_PAYMENT_REFERER__" + +Route itransact id "__MV_PAYMENT_ID__" + +Route netbilling id "__MV_PAYMENT_ID__" +Route netbilling secret "__MV_PAYMENT_SECRET__" + +Route signio id "__MV_PAYMENT_ID__" +Route signio secret "__MV_PAYMENT_SECRET__" +Route signio partner "__MV_PAYMENT_PARTNER__" +Route signio vendor "__MV_PAYMENT_VENDOR__" +Route signio host "__MV_PAYMENT_HOST__" + +Route skipjack id "__MV_PAYMENT_ID__" +Route skipjack partner "__MV_PAYMENT_PARTNER__" + +Route linkpoint id "__MV_PAYMENT_ID__" +Route linkpoint host "__MV_PAYMENT_HOST__" +Route linkpoint keyfile "__MV_PAYMENT_SECRET__" + +# This prevents a user from setting this value, you may want to unset +# this if you have user-selectable handling charges like insurance +FormIgnore mv_handling +# +# + +# Along these lines further, for better integrity and less chance of a +# user screwing up your order routes: +# Note that __ORDER_ROUTES__ is empty by default, default Route "default" +# is used with cascades +FormIgnore mv_order_route + +## This route places the order entry in the database. If you don't +## have an inventory table (or a userdb table for that matter) make +## sure you remove it from the list of "transactions" tables. + +Route log <<EOF + empty 1 + encrypt 0 + increment 0 + report etc/log_transaction + supplant 0 + track logs/log +EOF + +ifdef TRANSACTION_TABLES +Route log transactions '__TRANSACTION_TABLES__' +endif + +## This route copies the user if they requested that. We don't +## care (much) if it fails, so error_ok is set and failure will +## not cause the order to fail +Route copy_user <<EOF + empty 1 + error_ok 1 + encrypt 0 + increment 0 + report etc/mail_receipt + supplant 0 + track logs/log +EOF + +ParseVariables Yes +## This route emails the order to you unless email is set to "", +## and failsafe-logs the order report a couple of places +Route main <<EOF + attach 0 + credit_card 1 + default 1 + email '__ORDERS_TO__' + encrypt 0 + errors_to '__ORDERS_TO__' + pgp_cc_key "__PGP_KEY__" + pgp_key "__PGP_KEY__" + receipt etc/receipt.html + report etc/report + supplant 1 + individual_track orders + track logs/tracking.asc +EOF + +## This route emails the order to you unless email is set to "", +## and failsafe-logs the order report a couple of places +Route main_entry <<EOF + attach 0 + credit_card 1 + email '__ORDERS_TO__' + encrypt 0 + errors_to '__ORDERS_TO__' + pgp_cc_key "__PGP_KEY__" + pgp_key "__PGP_KEY__" + report etc/report + supplant 1 + individual_track orders + track logs/tracking.asc +EOF + + +# Order desk route run if entered from admin +# always +Route entry master 1 +Route entry cascade "log main_entry copy_user" +Route entry empty 1 +Route entry supplant 1 +Route entry no_receipt 1 +Route entry counter_tid logs/tid.counter +Route entry write_tables "inventory userdb transactions orderline" +Route entry transactions '__TRANSACTION_TABLES__' +Route entry email '__ORDERS_TO__' + +# Default route is run if no routes set, this should be last Route +# always +Route default master 1 +Route default cascade "log main copy_user" +Route default empty 1 +Route default supplant 1 +Route default counter_tid logs/tid.counter +Route default write_tables "inventory userdb transactions orderline" +Route default transactions '__TRANSACTION_TABLES__' +Route default email '__ORDERS_TO__' + +## Uncomment this if you want Routes read dynamically from DB +#Route default dynamic_routes 1 +## Uncomment this if you want ITL allowed in routes +#Route default expandable 1 + + +## Sest +SalesTax __TAXFIELD__ +TaxShipping __TAXSHIPPING__ + +OrderCounter etc/order.number +OrderLineLimit 200 +Profiles include/profiles/*.* + +## Sets the default as to whether items are aggregated or ordered +## on separate lines. Can be overridden with mv_separate_items=0 in +## URL or form. +SeparateItems no + +#==========================================================================# + +ifdef UI_TRAFFIC_STATS +TrackFile __UI_TRAFFIC_STATS__ +endif + +# Deal with customer click history. For example, after adding an item to +# the cart, the user can return to a specific search results page. +History 10 + +# Allow delivery of soft goods (downloadable files). +ActionMap deliver <<EOR +sub { + $Scratch->{deliverable} = $CGI->{mv_arg}; + $CGI->{mv_nextpage} = 'deliver'; + if(! $Session->{username} and $CGI->{mv_username}) { + $Tag->userdb('login'); + } + return 1; +} +EOR + +# Pricing setup +# +# If the user is logged in and is marked as a "dealer" (1 in the dealer +# field in the userdb database) then they are given quantity discounts +# based on price groups. (All products are in price group 1 as +# distributed.) If the quantity is 1, then pricing comes from the +# "wholesale" field in the products database. + +AutoModifier pricing:price_group + +# +# If the user is not a dealer (or not logged in) then pricing just comes +# from "price". Any quantity discounts will be set in the qN fields in the +# database, and are separate from dealer quantity discounts. +# + +Profile dealer <<EOR +{ + CommonAdjust => <<EOF, + + pricing:w5,w10:, + ;:wholesale, + ;:wholesale:mv_sku, + ;$, + ==:options +EOF + NonTaxableField => 'nontaxable', +} +EOR + +Profile distributor <<EOR +{ + CommonAdjust => <<EOF, + + pricing:w5,w10:, + ;:wholesale, + ;:wholesale:mv_sku, + ;$, + ==:options, + -10% +EOF + NonTaxableField => 'nontaxable', +} +EOR + +## This should match the default direct setting below this paragraph +Profile default CommonAdjust "pricing:q5,q10 ;:sale_price, ;:price, ;$, :related, ==:options" +Profile default NonTaxableField +Profile default PriceField 0 + +CommonAdjust pricing:q5,q10 ;:sale_price, ;:price, ;$, :related, ==:options +PriceField 0 + +## This sets the type of options pertaining to the product +OptionsEnable option_type + +## Finally, set which tables products can be ordered from +ProductFiles products variants + +#==========================================================================# + +## Tag which provides a CSS button-looking link +UserTag b-link Order href form +UserTag b-link HasEndTag +UserTag b-link addAttr +UserTag b-link Routine <<EOR +sub { + my ($page, $form, $opt, $anchor) = @_; + # use vars qw/$Tag $Scratch/; + if($Scratch->{no_javascript}) { + $opt->{extra} ||= 'style="font-size: smaller"'; + return $Tag->page($opt) . $anchor . '</a>'; + } + my $class = $opt->{class} || 'btn'; + my $url = $Tag->area($opt); + my $string = $Tag->filter('encode_entities', $anchor); + my @properties; + push @properties, "type=button"; + push @properties, qq{value="$string"}; + push @properties, qq{class="$class"}; + for(qw/style id name/) { + next unless $opt->{$_}; + push @properties, qq{$_="$opt->{$_}"}; + } + push @properties, qq{onClick="window.location='$url'"}; + push @properties, qq{onMouseOver="saveblink=window.status;window.status='$url'"}; + push @properties, qq{onMouseOut="window.status=saveblink"}; + if($opt->{title}) { + $opt->{title} = $Tag->filter('encode_entities', $opt->{title}); + } + else { + $opt->{title} = $string; + } + push @properties, qq{title="$opt->{title}"}; + push @properties, $opt->{extra} if $opt->{extra}; + + return qq{<input } . join(" ", @properties) . '>'; +} +EOR + +### Shipping setup. See Interchange docs -- you will want to change +### the origin postal code and default destination postal codes, for sure. +Shipping Postal default_geo 45056 +Shipping QueryUPS default_geo 45056 +Shipping default dir products/ship + +#Database 2ndDayAir ship/2ndDayAir.csv CSV +#Database 2ndDayAirAM ship/2ndDayAirAM.csv CSV +#Database 3DaySelect ship/3DaySelect.csv CSV +#Database Expedited ship/Expedited.csv CSV +#Database Express ship/Express.csv CSV +#Database Ground ship/Ground.csv CSV +#Database NextDayAir ship/NextDayAir.csv CSV +#Database NextDayAirSaver ship/NextDayAirSaver.csv CSV +#Database Xarea ship/Xarea.csv CSV +#Database Zone ship/Zone.csv CSV +#Database usps ship/usps.txt TAB +#Database Book ship/Book.txt TAB +#Database Priority ship/Priority.txt TAB +#Database air_pp ship/air_pp.txt TAB +#Database surf_pp ship/surf_pp.txt TAB +#Database ups_cache ship/ups_cache.txt __SQLDSN__ +#Database ups_cache AUTO_SEQUENCE ups_cache_seq +#Database ups_cache DEFAULT_TYPE varchar(12) +#Database ups_cache INDEX weight origin zip shipmode country + +include usertag/*.tag + +## Uncomment +MaxQuantityField inventory:quantity + +## Builds a series of hidden variables given an IC-style form +## specification, i.e. +## +## [hiddens] +## mv_action=refresh +## mv_nextpage=foo +## [/hiddens] +## +UserTag hiddens Interpolate 1 +UserTag hiddens HasEndTag +UserTag hiddens Routine <<EOR +sub { + my $block = shift; + my @lines = grep /\S/, split /\n/, $block; + + my @out; + for(@lines) { + s/\s+$//; + s/^\s+//; + my ($k,$v) = split /=/, $_, 2; + $v = $Tag->filter('encode_entities', $v); + push @out, qq{<input type=hidden name="$k" value="$v">}; + } + return join "\n", @out; +} +EOR + +## When uncommented, delivers a redirect if we get a request for an image +## DeliverImage Yes + +## Map a subroutine to happen if the page is not there +SpecialSub missing ncheck_category + +UserTag edisplay Alias error auto=1 class="alert alert-danger list-unstyled" +UserTag wdisplay Alias warnings auto=1 list_class="alert alert-success list-unstyled" +UserTag ecgi Alias cgi keep=1 filter=encode_entities name= + +CodeDef string2uri Filter +CodeDef string2uri Description Sanitize a string for use in a URL +CodeDef string2uri Routine <<EOR +sub { + my $val = shift; + $val =~ s|/|::|g; + $val =~ s|-|_|g; + $val =~ s|\s+|-|g; + return $val; +} +EOR + +CodeDef uri2string Filter +CodeDef uri2string Description Revert sanitized URL back to a string +CodeDef uri2string Routine <<EOR +sub { + my $val = shift; + $val =~ s|-| |g; + $val =~ s|_|-|g; + $val =~ s|::|/|g; + return $val; +} +EOR + +SearchProfile include/profiles/searchprofiles + +Jobs log __LOGDIR__/jobs.log +Jobs base_directory etc/jobs diff --git a/html/css/product.css b/html/css/product.css new file mode 100644 index 0000000..4655513 --- /dev/null +++ b/html/css/product.css @@ -0,0 +1,74 @@ +.container { + max-width: 640px; +} + +/* + * Custom translucent site header + */ + +.site-header { + background-color: rgba(0, 0, 0, .85); + -webkit-backdrop-filter: saturate(180%) blur(20px); + backdrop-filter: saturate(180%) blur(20px); +} +.site-header a { + color: #999; + transition: ease-in-out color .15s; +} +.site-header a:hover { + color: #fff; + text-decoration: none; +} + +/* + * Dummy devices (replace them with your own or something else entirely!) + */ + +.product-device { + position: absolute; + right: 10%; + bottom: -30%; + width: 300px; + height: 540px; + background-color: #333; + border-radius: 21px; + -webkit-transform: rotate(30deg); + transform: rotate(30deg); +} + +.product-device::before { + position: absolute; + top: 10%; + right: 10px; + bottom: 10%; + left: 10px; + content: ""; + background-color: rgba(255, 255, 255, .1); + border-radius: 5px; +} + +.product-device-2 { + top: -25%; + right: auto; + bottom: 0; + left: 5%; + background-color: #e5e5e5; +} + + +/* + * Extra utilities + */ + +.flex-equal > * { + -ms-flex: 1; + flex: 1; +} +@media (min-width: 640px) { + .flex-md-equal > * { + -ms-flex: 1; + flex: 1; + } +} + +.overflow-hidden { overflow: hidden; }
\ No newline at end of file diff --git a/namatoko.scss b/namatoko.scss new file mode 100644 index 0000000..701dc88 --- /dev/null +++ b/namatoko.scss @@ -0,0 +1,6 @@ +$thumbnail-border-width: 0; +$light: #fffffff6; +$grid-gutter-width: 0; +$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Roboto Mono", monospace !default; + +@import "bootstrap/scss/bootstrap"; diff --git a/pages/flypage.html b/pages/flypage.html new file mode 100644 index 0000000..094e5f3 --- /dev/null +++ b/pages/flypage.html @@ -0,0 +1,77 @@ +@_TOP_@ + <div class="container pt-5"> + <div class="container p-3"> + <img class="img-fluid" src="__IMAGE_DIR__/items/[item-field image]" alt="[item-description]"> + </div> + <!-- Product heading --> + <div class="container pl-3 pr-3"> + <h4>[item-description]</h4> + <h5 class="text-primary">[item-price]</h5> + <p class="text-muted text-monospace">[item-field prod_group] • [item-field category]</p> + </div>[fly-list code="[data session arg]"] + <form class="col" action="[area href="[if-item-field option_type][item-code][else]ord/checkout[/else][/if-item-field]"]" method="POST"> + <input type="hidden" name="mv_action" value="refresh"> + <input type="hidden" name="mv_order_item" value="[item-code]"> + <input type="hidden" name="mv_order_quantity" value="1"> + [form-session-id][edisplay show_var=0][if-item-field option_type][seti check_opt] + &success=ord/checkout + [query + list=1 + sql="select o_group,o_label from options where sku='[item-code]'" + ]mv_order_[sql-code]=mandatory [L]Need to select[/L] [sql-param o_label] + [/query][/seti] + <input type="hidden" name="mv_form_profile" value="check_opt"> + <!-- Product form control --> + <div class=" container pl-3 pr-3"> + <div class="row">[perl options] +my $elements = ''; +for my $option (@{$Tag->query({sql + => "SELECT o_group,o_label,o_value,o_widget FROM options WHERE sku='[item-code]' ORDER BY o_sort"})}) { + my $o_label = $Tag->loc('', $option->[1]); + $elements .= qq( + <div class="form-group pr-2"> + <label for="mv_order_$option->[0]">$o_label</label> + <select class="form-control" id="mv_order_$option->[0]" name="mv_order_$option->[0]">); + my @csvs = split(/,/, $option->[2]); + for my $csv (@csvs) { + my $variant = '[item-code]'; + my @pair = split(/=/, $csv); + my $label = $pair[1]; + my $default = ''; + if ($label =~ /\*/) { + $default = $Tag->loc('', substr($label, 0, length($label) - 1)); + } else { + $label = $Tag->loc('', $label); + } + $elements .= qq( + <option value="$pair[0]") + . ($default ? ' selected' : '') . '>' + . ($default ? $default : $label) + . '</option>'; + } + $elements .= qq( + </select> + </div>); + } + return $elements; + [/perl] + </div> + </div>[/if-item-field] + <!-- Call to action button --> + <div class="container p-3"> + <div class="row"> + <div class="col p-2"> + <button class="btn btn-outline-primary btn-lg btn-block" href="[area ord/basket]" type="submit">+ Keranjang</button> + </div> + <div class="col p-2"> + <button class="btn btn-primary btn-lg btn-block" href="[area ord/checkout]" type="submit">[L]Checkout[/L]</button> + </div> + </div> + </div> + </form>[/fly-list] + <!-- Product description --> + <div class="container p-3"> + <p class="text-muted text-monospace">[L]Description[/L]</p> + <p>[item-field comment]</p> + </div> + </div>__BOTTOM__ diff --git a/pages/ord/basket.html b/pages/ord/basket.html new file mode 100644 index 0000000..4778d6a --- /dev/null +++ b/pages/ord/basket.html @@ -0,0 +1,158 @@ +[tmp page_title]__COMPANY__ -- [L]Shopping Cart[/L][/tmp] + +@_TOP_@ +<!-- BEGIN CONTENT --> +<div class="container p-4"> + <!-- Hero Section--> + <div class="py-5"> + <div class="text-start"> + <h1>[L]Shopping Cart[/L]</h1> + </div> + </div> + <!-- End of Hero section --> + <div class="container g-5"> + <div class="container"> + <div> + [edisplay] + + [calc] + my $cname = $Config->{CookieName} || 'MV_SESSION_ID'; + $Scratch->{have_cookie} = $Tag->read_cookie($cname) + and delete $Scratch->{tried}; + return; + [/calc] + [if scratch have_cookie] + [elsif scratch tried] + [L CART_MSG1]You must have cookies set to leave the basket. Check out now or forever lose your shopping cart.[/L] + [/elsif] + [else] + [set tried]1[/set] + [bounce href="[area ord/basket]"] + [/else] + [/if] + + <form action="[process secure=1]" method=POST name="basket" class="form-horizontal" id="form"> + [form-session-id] + <input type="hidden" name="mv_doit" value="refresh"> + <input type="hidden" name="mv_orderpage" value="ord/basket"> + <input type="hidden" name="mv_nextpage" value="index"> + <input type="hidden" name="mv_nlines" value="[nitems lines=1]"> + + <div class="container"> + <div class="container font-weight-bold text-center p-4"> + <div class="row"> + <div class="col-md-5">[L]Description[/L]</div> + <div class="col-md-7 d-none d-md-block"> + <div class="row"> + <div class="col-md-3">[L]Price[/L]</div> + <div class="col-md-4">[L]Quantity[/L]</div> + <div class="col-md-3">[L]Total[/L]</div> + <div class="col-md-2"></div> + </div> + </div> + </div> + </div> + <div> + + [if items] + [then] + [item-list] + + [item-calc] + #Log("Checking master item $master"); + $row_class = ++$count % 2 ? 'cartnorm' : 'cartalt'; + my $item = '[item-increment]'; + my $up = q{[item-data merchandising upsell_to]}; + my $cr = q{[item-data merchandising cross_sell]}; + $upsell_remove{'[item-code]'} = 1; + $cross_remove{'[item-code]'} = 1; + my %seen = ( '' => 1 ); + + $Scratch->{upsell} .= " $up" if $up; + $Scratch->{cross_codes} .= " $cr" if $cr; + my @up = split /\s+/, $Scratch->{upsell}; + my @cr = split /\s+/, $Scratch->{cross_codes}; + @up = grep ( (!$seen{$_}++ && ! $upsell_remove{$_}), @up); + @cr = grep ( (!$seen{$_}++ && ! $cross_remove{$_}), @cr); + $Scratch->{upsell} = join " ", @up; + $Scratch->{cross_codes} = join " ", @cr; + return; + [/item-calc] + + <!-- Product--> + <div class="container pb-4"> + <div class="card p-4"> + <div class="row d-flex align-items-center text-left text-md-center"> + <div class="col-12 col-md-5"><a class="cart-remove close mt-3 d-md-none" href="#"><i class="fa fa-times"></i></a> + <div class="d-flex align-items-center"><a href="[area href="[item-sku]"]">[image src="[item-field image]" imagesubdir=items makesize="80x80" extra='class="cart-item-img" alt=""']</a> + <div class="text-left"><a href="[area href="[item-sku]"]"><strong>[item-description]</strong></a><br><span class="text-muted text-sm">Size: Large</span><br><span class="text-muted text-sm">Colour: Green</span> + </div> + </div> + </div> + <div class="col-12 col-md-7 mt-4 mt-md-0"> + <div class="row align-items-center"> + <div class="col-md-3"> + <div class="row"> + <div class="col-6 d-md-none text-muted">[L]Price[/L]</div> + <div class="col-6 col-md-12 text-right text-md-center">[item-discount-price]</div> + </div> + </div> + <div class="col-md-4"> + <div class="row align-items-center"> + <div class="d-md-none col-7 col-sm-9 text-muted">[L]Quantity[/L]</div> + <div class="col-5 col-sm-3 col-md-12"> + <div class="d-flex align-items-center"> + <div class="btn btn-items btn-items-decrease" onclick="document.getElementById('[item-quantity-name]').value--; document.getElementById('form').submit()">-</div> + <input class="form-control text-center border-0 border-md input-items" type="text" value="[item-quantity]" name="[item-quantity-name]" id="[item-quantity-name]" onchange="this.form.submit()"> + <div class="btn btn-items btn-items-increase" onclick="document.getElementById('[item-quantity-name]').value++; document.getElementById('form').submit()">+</div> + </div> + </div> + </div> + </div> + <div class="col-md-3"> + <div class="row"> + <div class="col-6 d-md-none text-muted">[L]Total price[/L]</div> + <div class="col-6 col-md-12 text-right text-md-center">[item-discount-subtotal]</div> + </div> + </div> + <div class="col-2 d-none d-md-block text-center"> + <a class="cart-remove" href="#" onclick="document.getElementById('[item-quantity-name]').value = 0; document.getElementById('form').submit()"><span class="material-icons-outlined">close</span> + </a> + </div> + </div> + </div> + </div> + </div> + </div> + [/item-list] + [/then] + [/if] + <!-- End of product --> + </div> + </div> + <div class="card bg-light border-dark"> + <div class="card-header "> + <h6 class="text-uppercase mb-0">[L]Order Summary[/L]</h6> + </div> + <div class="card-body"> + <div class="card-text"> + <span>[L]Subtotal[/L]</span><span class="float-right">[subtotal]</span> + </div> + </div> + <div class="card-footer"> + <strong><span>Total</span><span class="float-right">[total-cost]</span></strong> + </div> + </div> + <div class="mt-4"> + <div class="d-flex justify-content-between flex-column flex-lg-row"> + <a class="btn btn-lg btn-link text-muted" href="[area index]">[L]Continue Shopping[/L]</a> + <a class="btn btn-lg btn-primary" href="[area ord/checkout]">[L]Proceed to checkout[/L]</a> + </div> + </div> + </form> + </div> + </div> + </div> +</div> +<!-- END CONTENT --> +@_BOTTOM_@
\ No newline at end of file diff --git a/pages/ord/checkout.html b/pages/ord/checkout.html new file mode 100644 index 0000000..53963ae --- /dev/null +++ b/pages/ord/checkout.html @@ -0,0 +1,138 @@ +[tmp page_title]__COMPANY__ -- [L]Checkout[/L][/tmp] +[tmp display_class]noleft[/tmp] +[tmpn onepage]1[/tmpn] + +[include include/checkout/initialization] + +@_TOP_@ + <!-- BEGIN CONTENT --> + <div class="container p-4"> + <!-- Hero Section--> + <div class="py-5"> + <h2>[L]Checkout form[/L]</h2> + [if session logged_in] + <p class="lead">[L]Please verify the information below and click the <b>'Place Order'</b> button to process your order.[/L]</p> + [include include/checkout/login_form][/else][/if][if !variable NO_TAX_POPUP] + [include include/checkout/tax_popup][/if] + [edisplay show_label=1 show_var=0 keep=1] + </div> + <!-- End Hero section--> + <div class="g-5"> + + <form action="[area href=@@MV_PAGE@@ secure=1]" method="POST" name="checkout"> + [form-session-id] + <input type="hidden" name="mv_action" value="refresh"/> + <div> + <h4 class="d-flex justify-content-between align-items-center mb-3">[L]Order review[/L]</h4> + <ul class="list-group mb-3"> + <!-- Product Item --> + [if items][then][item-list][item-calc] + #Log("Checking master item $master"); + $row_class = ++$count % 2 ? 'cartnorm' : 'cartalt'; + my $item = '[item-increment]'; + my $up = q{[item-data merchandising upsell_to]}; + my $cr = q{[item-data merchandising cross_sell]}; + $upsell_remove{'[item-code]'} = 1; + $cross_remove{'[item-code]'} = 1; + my %seen = ( '' => 1 ); + $Scratch->{upsell} .= " $up" if $up; + $Scratch->{cross_codes} .= " $cr" if $cr; + my @up = split /\s+/, $Scratch->{upsell}; + my @cr = split /\s+/, $Scratch->{cross_codes}; + @up = grep ( (!$seen{$_}++ && ! $upsell_remove{$_}), @up); + @cr = grep ( (!$seen{$_}++ && ! $cross_remove{$_}), @cr); + $Scratch->{upsell} = join " ", @up; + $Scratch->{cross_codes} = join " ", @cr; + return; + [/item-calc] + <li class="list-group-item d-flex justify-content-between lh-sm"> + <div> + <a href="[area href="[item-sku]"]"><h6 class="my-0">[item-description]</h6></a> + <small class="text-muted">[item-discount-price]</small> + <small class="text-muted">x [item-quantity]</small> + </div> + <span class="text-muted">[item-discount-subtotal]</</span> + </li> + [/item-list] + <!-- End of Product Item --> + <li class="list-group-item d-flex justify-content-between"> + <span>Subtotal</span> + <strong>[subtotal]</strong> + </li> + [/then][/if] + </ul> + </div> + <!-- Shipping Address --> + <div> + <h4 class="mb-3">Shipping</h4> + [include include/checkout/shipping_address] + </div> + <!-- End of Shipping Address --> + <!-- Billing Address --> + [include include/checkout/billing_address] + <!-- End of Billing Address --> + <!-- Delivery Method --> + <div> + <h4>Delivery method</h4> + <!-- Delivery option--> + <div class="row"> + <div class="form-group col-md-6 d-flex align-items-center"> + <input type="radio" name="mv_shipmode" id="option0" value="cpa" checked/> + <label class="ml-3" for="option0"> + <strong class="d-block text-uppercase mb-2">[shipping-desc]</strong> + <span class="text-muted text-sm"> + [L]Get it right on next day - fastest option possible.[/L] + </span> + </label> + </div> + </div> + <!-- End of delivery option --> + </div> + <!-- End of Delivery Method --> + <!-- Payment Method --> + <div> + <h4 class="mb-3">Payment method</h4> + <!-- Payment Option--> + <div class="row"> + <div class="form-group col-md-6 d-flex align-items-center"> + <input type="radio" name="mv_order_profile" value="purchase_order" id="payment-method-1" checked/> + <label class="ml-3" for="payment-method-1"> + <strong class="d-block text-uppercase mb-2">[L]Manual Transfer[/L]</strong> + <span class="text-muted text-sm">[L]Please make payments by using the bank transfer method to BCA 12345678 a/n ABCDEFG.[/L]</span> + </label> + </div> + </div> + <!-- /Payment Option--> + </div> + <!-- End of Payment Method --> + <!-- Order summary --> + <div> + <h4 class="mb-3">Order Summary</h4> + <div> + <p class="text-sm">Shipping and additional costs are calculated based on values you have entered.</p> + <ul class="order-summary mb-0 list-unstyled"> + <li class="order-summary-item"><span>[L]Subtotal[/L]</span><span>[subtotal]</span></li> + <li class="order-summary-item"><span>[L]Shipping[/L]</span><span>[shipping]</span></li> + <li class="order-summary-item border-0"><span>[L]Total[/L]</span><strong class="order-summary-total">[total-cost]</strong></li> + </ul> + </div> + </div> + + <!-- End of Order Summary --> + <div class="d-grid gap-2 d-md-block mb-5">[if items] + + [button + text="[L]Place Order[/L]" + wait-text="-- [L]Wait[/L] --" + class="btn btn-primary btn-lg btn-block" + ] mv_todo=submit + [/button][else] + <b>[L]No items in your shopping cart![/L]</b>[/else][/if] + </div> + </form> + + </div> + [tmp clear_errors][error all=1 comment="Clear errors"][/tmp] + </div> + <!-- END CONTENT --> +__BOTTOM__ diff --git a/pages/results.html b/pages/results.html new file mode 100644 index 0000000..aea3ff7 --- /dev/null +++ b/pages/results.html @@ -0,0 +1,92 @@ +@_TOP_@ + + <!-- Catalog Content --> + <div class="container pt-5"> + <div class="row text-center text-lg-left pt-2">[search-region][on-match][tmp meta_header][calcn] + return unless $Values->{more_link}; + (my $more_page = $Session->{last_url}) =~ s|^/.*/([^\..]+)(?:\.html)?|$1|; + if ($more_page =~ /^(Next|Prev)/) { + $Tag->tag({ op => 'header' }, qq{Cache-control: no-cache\nCache-control: no-store\nPragma: no-cache\nExpires: 0\nContent-type: text/html}); + } + ## $mc set above. + my $ml = $Values->{mv_matchlimit} || ''; + my $fm = $Values->{mv_first_match} || ''; + my ($canon, $rel_prev, $rel_next); + #Debug("more_page=$more_page, ml=$ml, fm=$fm, mc=$mc"); + my ($prev, $next); + REL: { + last REL if $mc < $ml; + if ($fm == 0 || $fm == 1) { # first page + $rel_next = $Values->{more_link} . '/2'; + } elsif (($mc - $fm - $ml) <= 0) { # last page + $prev = sprintf("%.0f", (($fm+$ml) / $ml) - 1); + $rel_prev = $Values->{more_link} . '/' . ($prev ? $prev : '1'); + } else { # page 2 to second-to-last + $prev = sprintf("%.0f", ($fm / $ml)); + $next = $prev + 2; + $rel_prev = $Values->{more_link} . "/$prev"; + $rel_next = $Values->{more_link} . "/$next"; + } + } + CANON: { + last CANON if $more_page !~ /^1|(Next|Prev)/; + if ($more_page eq '1') { + $canon = ''; + } else { + $canon = sprintf("%.0f", ($fm+$ml) / $ml); + $canon = '' if $canon == 1; + } + $canon = $Values->{more_link} . ($canon ? "/$canon" : ''); + } + my @out; + my $url = ($Variable->{SAMPLEURL} . $Variable->{CGI_URL}) || ''; + my $ext = $Scratch->{mv_add_dot_html} ? '.html' : ''; + $url !~ /\/$/ and $url .= '/'; + push @out, qq{<link rel="prev" href="$url$rel_prev$ext">} if $rel_prev; + push @out, qq{<link rel="next" href="$url$rel_next$ext">} if $rel_next; + $Tag->tmp('rel_next', 1) if $rel_next; + push @out, qq{<link rel="canonical" href="$url$canon$ext">} if $canon; + if ($prev || $next) { $Scratch->{page_title} = $Scratch->{page_title} . ' - Page ' . (($prev || $next)+1) }; + return "\t" . join "\n\t", @out; + [/calcn][/tmp][/on-match][tmp bw_more][more-list][first-anchor][/first-anchor][last-anchor][/last-anchor][link-template]<li><a href="[area href=|[value more_link]/$ANCHOR$|]">$ANCHOR$</a></li>[/link-template] + <nav> + <ul class="pagination"> + {PREV_LINK}{MORE_LIST}[if scratch rel_next]{NEXT_LINK}[/if] + </ul> + </nav> + [/more-list][/tmp][calc] + my $more = $Scratch->{bw_more}; + $more and $more =~ s,<strong>,<li class="active"><a href="#">,; + $more and $more =~ s,</strong> *,</a></li>,g; + $Scratch->{bw_more} = $more; + return $Values->{more_link} ? $more : undef; + [/calc][search-list] + <div class="col-lg-4 col-md-4"> + <a href="[area [item-code]]" class="d-block h-100"> + <img src="__IMAGE_DIR__/thumb/[item-field thumb]" class="img-fluid img-thumbnail" alt="[item-description]" title="[item-code]"> + </a> + </div>[/search-list][no-match] + <div>[comment] + Don't show the search string if it was empty, since it looks weird + and the user will see the error below anyway. + [/comment][tmp matchstring][value-extended name=mv_searchspec joiner=" | " filter=encode_entities][/tmp][if scratch matchstring] + [msg arg.0="[scratch matchstring]"]Sorry, no matches for <B>%s</B>.[/msg] + [/if][if value mv_search_error] + <B> + [L]Errors[/L]: + <BR>[value-extended name=mv_search_error joiner="<BR>" filter=encode_entities] + </B> + [/if] + </div> + [/no-match][if value more_link][scratch bw_more][else][more-list] + <div class=morelist> + [msg arg.0="[matches]" arg.1="[match-count]"]Matches %s of %s found.[/msg] + [link-template]<a href="$URL$" target="_top" class="morelink">$ANCHOR$</a>[/link-template] + <BR>[more]<BR> + </div> + [/more-list] + [/else] + [/if][/search-region] + </div> + </div> + <!-- .End of Catalog Content -->__BOTTOM__ diff --git a/templates/layout/noleft b/templates/layout/noleft new file mode 100644 index 0000000..9c5e712 --- /dev/null +++ b/templates/layout/noleft @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <meta name="description" content=""> + <meta name="author" content="namatoko"> + <meta name="generator" content="namatoko"> + <title>__COMPANY__</title> + <link rel="stylesheet" type="text/css" href=""> + <!-- Bootstrap Core CSS--> + <link href="__WWW_DIR__/css/bootstrap.min.css" rel="stylesheet"> + <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/> + <style> + .bd-placeholder-img { + font-size: 1.125rem; + text-anchor: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + @media (min-width: 768px) { + .bd-placeholder-img-lg { + font-size: 3.5rem; + } + } + </style> + <link href="__WWW_DIR__/css/product.css" rel="stylesheet"> +</head> +<body> + <!-- Navbar --> + <nav class="navbar navbar-light bg-light fixed-top"> + <div class="container d-flex justify-content-between"> + <a href="#" class="navbar-brand d-flex align-items-center"> + <img src="__IMAGE_DIR__/__LOGO__" width="30" height="30" class="rounded-circle d-inline-block align-top mr-2" alt=""> + __COMPANY__ + </a> + <div class="nav-item"> + <a class="navbar-icon-link" href="[area ord/basket]"> + <span class="material-icons-outlined"> + shopping_bag + </span> + </a> + </div> + </div> + </nav> + <!-- End of Navbar --> + {{:DEFAULT}} +</body> +</html> diff --git a/variables/BOTTOM b/variables/BOTTOM new file mode 100644 index 0000000..42a82a5 --- /dev/null +++ b/variables/BOTTOM @@ -0,0 +1,16 @@ +[output name=copyright] +__COPYRIGHT__ + +[output name=edit_controls] +__ADL_PAGE__ + +[unpack] +[include file="templates/layout/[either][scratch display_class][or]noleft[/either]"] +[/unpack] +[comment] Clear these puppies [/comment][calc] + for (qw/ + page_title + display_class + /) { delete $Scratch->{$_} } + return; +[/calc] |