Properly adding v-model support to a component (vue.js)

This is one of those things I always forget that’s hard to google properly.

In a child component, adding v-model support starts with a ‘value’ prop. This is the input value arriving at the child component. In the example below, I’m expecting to work with an array.

  props: {
    value: {
      default: () => [],
      type: Array,
    },
  },

We don’t want to mutate the underlying passed in value when it gets changed in the component, so the value prop is cloned into a data property.

  data() {
    return {
      editingRow: false,
      sections: [...this.value]
    }
  },

From here on in, we manipulate only the sections data, never touching the input value prop.

 saveSection(){
      this.sections.push(this.editingRow);
      this.editingRow = false;
}

When it’s time to notify the parent and sync the changes (on clicking save, or just on a practical point within the app), we emit a ‘input’ event with the new contents of the value.

this.$emit('input', this.sections);

Copying Small startups – Food Booking Software.

Food Booking Software – mind map.

Eating at a pub involves lining up with the other patrons, reviewing the menu, speaking back the order and any alterations to staff who enter it in the system and ring up a price.

The customer pays for the order and the order goes to the back-restaurant/make-bench to be made.

A modern approach (ME&U app?) allows the pub to create a restaurant in the app and add their menu items, sorted by categories. Products have a price (alternatively a second price for other purposes, e.g. members). Products have extensive alterations.

The pub prints labels for each desk that have the site code and table code embedded in a QR code for the customer to scan. The business can alternatively print nice labels and have them sent out – the labels attach to a cutlery basket, stick to the table, The customer gets taken to the app with a querystring that can load up the restaurant and the menu.

The customer fills a ‘cart’ on the site and ultimately checks out. The customer needs to make payment when they submit an order. The restaurant can begin cooking the order for the customer, via a POS integration.

If the customer does not understand, gets stuck, or chooses not to use the app, they can still proceed ‘the old way’.

The customer can continue to shop on their app for drinks and order and pay online.

The customer can use the receipt link SMS’ed to their phone for disputes etc.

If payment is through the app, a clip will be paid to “Ordering app”.

The ‘pub’ needs customised labels for tables and an easy way to re-order or re-create their own. All QR codes should be easily reproducible.

The orders only make it to the back kitchen by a POS integration or a receipt printer on site.

The pub has a dashboard they can use, but this is not a key part of their use of the platform (e.g. they don’t need to get orders out of it, etc). They might sign in for refunds and billing enquiries, check payment settlements, update menus (if not POS integrated).

The obvious first step is to get in touch with a restaurant manager or owner and run the idea by them – does it sound like it’ll work?

Unoriginal Side Project Examples

No more original side projects.

This is an ongoing article of unoriginal side project ideas that could be executed. I don’t know what I’m doing, I’m just documenting this so I can find it again later.

The unoriginal side projects will follow a bit of a formula – having many elements in common. I’ll keep revisiting the article and adding examples.

Certain SaaS products go “Enterprise” or upmarket and leave a hole that can be filled to deal with smaller customers. They end up full of thousands of features, and there may be an underserved market of users who could be paying for a much lighter product.

They end up as “Request a demo” or “get more information”.

“Ha, twitter!? I could write that in a weekend!”

On reflecting on the enterprise behemoths below it looks like I’m doing the old “Ha, twitter!? I could write that in a weekend!”. It’s not that. I want to take an enterprise behemoth, slicing it back down to what it used to be, and build that.

Any one will still take me months of nights, realistically. I’ve got a full time job and a big family that both take priority, but also a strong desire to stop trading hours for money.
I’ll build up a stack I can re-use, find some valid and invalid assumptions, ship some products, and keep moving the goalposts as it goes.

Who’s going to buy the stripped out tools?

The target market for any of these projects will be a person with an online or offline business who already knows they need a <SaaS for X> (where X is “Trust widget”, “Support ticket tool”, …).
I don’t want to convince them they need one – I suck at that. I just need to convince them that this will also generate support tickets, doing much the same shit as Zendesk. They’re not going to find me in position 1 on google – that’ll always be Uservoice. They’re going to find me on page 2 of google (after abandoning all the “Schedule a demo!” shit), read a few articles, watch a video and create an account on the spot. Sometimes, hopefully.

Follows is my incomplete list of up-market enterprise SAML HIPAA compliant “schedule a call with our executive integration team” bullshit unicorns that I think have an underserved lower end of the market.

Ideally, I’d pad this list out with some smaller stuff, but hey, I’m starting here.

Help Desk Software

Customers email [email protected] and start a conversation thread with one or multiple support agents.

Customers use live chat

A knowledgebase is slowly created, which becomes SEO long tail bait, reference material, internal bible, and inbound lead generation.

Zendesk $5/agent/mo to $19/agent/mo (Max $99/agent/mo).

HelpScout $20/agent/mo to $35/agent/mo

Feature Request Software

Customers visit a feature request leaderboard, and it helps drive feature development of the SaaS.

In theory helps drive development roadmap. Gives users a place to help each other, self diagnose, request features.

uservoice is the big one ($499+/mo.). Loop, roadmap.space, etc are others.

Forms Software

Users can fill out a form, and the data is shipped to the form owner or a google sheet.

The form has many field types, including payments, maps, etc. New field types can be added regularly.

Paperform (12.50/mo to 32.50/mo, up to 82.50/mo)

Spreadsheet Software

Users fill data into spreadsheets, either with forms or spreadsheet view. They can link the sheets together, export to CSV, use as REST API.

Airtable $10-$20/user/mo. Airtable is a $X00B company, but 99% of the usefulness (that I can see) is linked spreadsheet rows that you can share with co-workers and devs.

Trust and Reviews

When an order is shipped, the trust server sends an email to the customer in a few days asking them to review the products and review the website.

The customer can review both and the data flows both to the website and to a list of the products.

Trustpilot $189/mo

Loyalty and Rewards

Award points and credits for purchases to a customers account. Customer can spend the points and credits on checkout.

loyaltylion: $159/mo

Things that unoriginal side projects should have in common

It’s a long title. I want to execute more than one unoriginal side project, so as many factors should be shared as possible, as easily as possible.

I’m not making original side projects anymore – I’m hunting for financially viable ideas that *have already been executed*.

I’ll cut down the scope and clone them as quickly as possible, and set them free. Each project will get sharper, build on the tools from the last, and eventually they will start finding a base.

Here’s a list of things that all of my unoriginal side projects will have.

  • They start free or very cheap, and slowly boil the frog (apparently that’s not a real thing).
  • A user can join as soon as they land on the site, and from every page on the site, and easily add the rest of their team. There’s access levels for different users (Owner, Editor, Viewer, for example).
  • Users don’t have remember an email and password – can sign in with google, github, and office 365. There is email & password signin though.
  • Billing uses Stripe Billing pre-built tools, to save writing that whole layer.
  • From the get-go, users get a subdomain for their tenant/team. E.g. acme.unoriginalsaas.com
  • All of these unoriginal SaaS’s are an MVP. Throwing mud until it sticks. They must be serverless or zero maintenance. Not buying a domain name, standing up a server, buying a g email address and all that shit for an app that in all likelihood will have a quick death. It takes 6 months for a side project to get some SEO results/organic visitors cranking, and I don’t want dollars burning away in the process.
  • If e-commerce related, they are shopify-first.
  • Comprehensive getting started docs and videos.
  • They’re easy to cast a net with well targeted SEO. Templates, recipes, case studies, documentation.
  • The marketing site is very easy to sign into and quickly add an article (e.g. wordpress level of 5-minute-article)
  • All are API first, and can be consumed as an API (assuming there’s comprehensive documentation). This allows a faster road to app stores and integrations.
  • Where possible, the tools will use each other. E.g. A support ticket tool will be useful to all other platforms
  • They need to be delivered quick. 3 month (at night time, not full time!) build, a month of documentation and SEO.

No more original side projects.

I had a thought tonight while out for a walk. If I wanted to open a takeaway shop, I’d start keeping track of the ones that looked like they were going well, and basically copy the execution model.

A coffee shop, a burger shop, or a bakery. Maybe a sushi store. There’d be no real ultra unique secret sauce typically, and I sure as hell wouldn’t spend years of my life waiting for the right type of takeaway shop to appear in my mind.

I’m not sure why I’ve spent years coming up with an original idea for a side project, executing it quickly, trying to work out a sales ‘message’, and doing the slow grind to find users. It’s just buying lottery tickets and hoping for the best.

First unique idea: an app builder for ecommerce stores.

I built “WooToApp”, an app builder for ecommerce stores. It didn’t exist at the end of the market that I wanted to target.

There were HUGE technical limitations and problems, and I felt like I was really innovating when architecting secure payments, secure orders, and a true drag and drop mobile app store builder for ecommerce stores.

I spent so long fucking around and working on techniques. Writing a piece of code and finding issues and re-writing.

Some technical limitations I just didn’t have enough time to reliably overcome. How can a user sign up instantly and have the app on their phone? I spent, I think, hundreds of hours on this.

I never killed it off. Just stopped iterating one day and it slowly died itself, never returning a dollar.

Second unique idea – dropshipping facilitation

I built “Elvenda” with my brother in law, a dropshipping enabler. Supplier signs up and adds their product catalog. Dropshipper signs up and imports products. Happy days, we can take a clip!

Except for all the problems that we had to discover, etch out solutions and execute ourselves. With no revenue or help, and no time.

What happens if fake products get shipped? How do returns happen? What platforms should we support? What formats can you import from? How the hell do we sell the idea to a “real” supplier?

We haven’t killed this off, and likely won’t. There’s just so much to do.

Uncool ideas – copying shit that is working

In 2016, I did some integration work for a brand new payment gateway called “AfterPay”. It didn’t support my clients store platform. The PDF they supplied as API docs was pretty rough and incomplete.

I quoted 12 hours to integrate AfterPay in this guys store and busted my ass. His sales increased overnight. Within a few weeks, 30% of his orders were using this new payment method, and his average order value was far higher. It was stupid expensive too, he was paying 5.5% + 30c on transactions.

The PDF documentation was so bad that by the time the integration was finished, I’d reverse engineered half of the API with postman. I got it.

I decided there’s nothing to it, and took 2 weeks off from contracting and wrote an almost exact clone of everything I could see. All it did was charge the end customer with Stripe on a cron job, and send me an email telling me how much to pay the merchant the next day.

I approached a client and also a friend with ecommerce stores and asked them to add my payment gateway, “PayItLater”.

Within weeks, the gateway was turning over so much money that my wife and I were out of cash. It was the most stressful 6 months of my life, but the nasty 2 week clone was working.

It works like this: A customer of the ecommerce store paid with ‘PayItLater’ for their $100 purchase. We paid the ecommerce store $94.20 ($100 – 5.5% – 30c), and basically hoped like hell that we’d recover the $100 from the customers credit card. The payment comes out in 4 payments over the next 8 weeks – it’s an addictive model, and .. Everybody knows about AfterPay now. They didn’t then.

Some guesswork modeling suggested 2% of people would be bad actors. I did some modeling on late fees and return customers as well that I don’t remember. We didn’t have any machine learning, historical data, AI or identity verification like AfterPay did. I just assumed everyone was a good actor, and banned their mobile number if they weren’t.

Since my phone was full of transactions every morning, I added a postcode to the transaction email. If we had more than 2 bad actors from a postcode the whole area would get banned. “Sorry, PayItLater is unavailable right now”.

As an aside, I remember a “Fuck Warrnambool” comment in the codebase – people in rural Victoria were somehow ripping me off.

We had no money, and our best guess was that PayItLater was making money. At any point in time if we added up the money that was out with merchants (that we were waiting to collect from their customers), deducted 2% for bad actors, it looked like it was creeping upwards ever so slowly.

I had $20,000 of my family’s money in PayItLater, and I’ve never been so stressed in all my life. I finally gave in one day – I couldn’t afford to grow it (I had $0 in real terms in the bank!) and had no idea how to take on money. I wasn’t having any luck with family and friends, and really didn’t even know how to ask.

In January 2017, we flicked the switch and turned off PayItLater. Customers could not check out with it anymore. The $20,000 we’d invested in PayItLater 6 months earlier slowly dripped back in and we ended up with $30,000. I sold the codebase to somebody who is still operating the business.

Where was I with the uncool business concept?

PayItLater worked, and was working. There was absolutely 0 original ideas and insight. I reverse engineered the plugin, had a best guess at how the backend worked, made the same or similar flows and went and got a customer.

There was no tricky decisions. No architecting flows. No guessing how to handle refunds. Not sure? Check AfterPay. Need a merchant agreement? Check AfterPay. What do I need to tell customers when a transaction is successful? Check AfterPay.

Why punish ourselves coming up with original ideas?

I don’t want to build Tesla or Uber, carving out a unique piece of history and making a huge dent on the world. I want Friday lunch at the beach and sleep ins on Monday.

I’m not doing original ideas anymore. You want to start a takeaway shop? You buy a coffee machine, a cash register, get a coke fridge and find a supplier of muffins.

You don’t scour the world looking for original ideas or try to come up with a meat free hamburger in your spare time. Nobody would buy it.

Next time, I’m picking something with a revenue model, that clearly makes money, that people have heard about, that is overpriced.

I’ll chop it down, make it cheap, easy to sign in and use, and easy for me to sell.

It’s not going to be technically interesting, ground-breaking, and there’s going to be no complex decisions involved.

I’m finding the boring profitable downtown coffee shop of SaaS tools and opening another one down the road.

Moving from Gmail to Hey

As part of a long effort to de-google where practical, I’ve moved away from Gmail to Hey.com email service.

The problem with decoupling anything online from Google is twofold –
1. all of your stuff already lives there – I’ve had a Gmail account for 16 years. It’s all perfectly designed to keep you in the flywheel, letting it capture your information.
2. the services are best of breed. Gmail is pretty good. Fast, works everywhere, filters nicely into subcategories.

I have no idea how to get the last 16 years of data out, in a searchable, indexed, private way. That’s for another day. This post is about futureproofing the transition to Hey.

The temptation to just set up an auto forward from Gmail to Hey won’t fix the problem. You won’t update your email address, your personal information won’t be untethered from Google, and you’ll be paying the Hey folks $99 a year for a pretty thin email client.

Do it this way instead:

Step 1: Create a new mail label: “To Be Ported”.

In Settings > See all Settings > Labels, create a new label “To Be Ported”.

In Settings > “Filters and blocked addresses”, create a new filter. If it was sent to you, skip the inbox and move it to “To Be Ported”.

Step 2: Start filtering/unsubscribing/etc.

This takes forever. Every single email you receive is either shit you don’t care about, something you should update, something you can’t update, or something that’s not worth updating yet.

Next to the new label “To Be Ported”, click the 3 dots and add 4 new sub labels. Cannot port, Irrelevant, Ported, Unsubscribed.

Step 2.a: Shit you don’t care about

All the mailing list subscriptions you’ve accumulated over the last 10+ years – open them one by one as they appear in the “To Be Ported” label, unsubscribe from them, and move them to the sub-label called ‘Unsubscribed’. That’s 90% of them solved by quantity.

Step 2.b: Pretty good newsletters

About half of these will allow you to click ‘Update preferences’ near the bottom of the email and change your email address on file. Of these, about half will actually work.

Generally, you’ll need to unsubscribe and then resubscribe with your new Hey.com email address.

Move all these emails to the “Ported” label – just drag them.

Step 2.c: The stuff you can’t port

I have no idea if I can port my icloud identity away from my gmail account. Didn’t try, not worth the hassle. Also, the emails they send me are junk. Drag these to the “Cannot Port” label. The only use for this is keeping the “To Be Ported” label empty, so you can deal with emails as they arrive.

Step 2.d: The too hard basket

My wife will probably never update my email contact. I could get around to sorting that out, but I won’t. I drop those emails in here since they are important to me but not ported.

Step 3: Once the queue dies down

I’m getting there. Once the ‘To Be Ported’ inbox slows to a dribble of new emails, it’s time for you to set up that auto forwarder. This means when a random old contact from 5 years ago emails you, it’ll go straight to the Hey.com imbox.

Create a new filter (Settings > “Filters and blocked addresses”) and send everything on over to the Hey.com address.

Step 4: Somehow get that data out of Gmail

Not sure of this yet. Enjoy your new email!

Firebase functions and CORS

A simple firebase http function template:

// accessible at https://us-central1-<app name>.cloudfunctions.net/testEndpoint
exports.testEndpoint = functions.https.onRequest(req, res) => {
  if (cors(req, res)) {
    res.end()
    return
  }

  // do something

  // make sure to 'end' the request or it'll time out
  res.send(true)
  res.end()
})

We need to handle CORS requests (see cors() above), because the browser will send an OPTIONS request first to see what’s accepted by the endpoint. Your function handler in this case will receive an empty body, throw a bunch of errors, and generally waste a ton of time.

A simple CORS handler template:

const cors = (req, res) => {
  const ALLOWED_ORIGINS = [
    'http://localhost:3000',
    'https://app.domain.com'
  ]

  if (ALLOWED_ORIGINS.includes(req.headers.origin)) {
    res.set('Access-Control-Allow-Origin', req.headers.origin)
    res.set('Access-Control-Allow-Headers', '*')
  }

  if (req.method === 'OPTIONS') {
    res.end()
    return true
  }
  return false
}

If it’s an OPTIONS request, send back the approved origin and end the response (via true response).

If it’s a different type of request, set the approved origin and continue (via false response).

Quick/Easy Laravel/Docker

Source: https://hub.docker.com/r/bitnami/laravel

Commands:

cd toys (projects folder)

mkdir saml && cd saml (a bare saml project)

curl -LO https://raw.githubusercontent.com/bitnami/bitnami-docker-laravel/master/docker-compose.ymlcurl -LO https://raw.githubusercontent.com/bitnami/bitnami-docker-laravel/master/docker-compose.yml

docker-compose up

Connect to the dev server over SSH in vscode and access at http://devserver:3000/

A blog for Emmett

It may come back to bite us. I made Emmett a WordPress blog tonight, a new outlet for his creative energy that so far is spent on long term minecraft trap strategies.

He’s 6, and it’s hard to find productive outlets for him. He gets tunnel vision about a single project at any one time.

So far, he’s added a post about home schooling and a general images post. I hope it consumes him like Minecraft does.

Emmett from Bargo’s blog.

Nuxt.js – redirect

The router docs are a little bit muddy with redirects.

Push the new route onto the stack to redirect.

this.$router.push(`/users/${id}`)

Replace the route on the stack to replace in the browser.

this.$router.replace(`/users/${id}`)

Extra: Updating querystring

Changes the querystring in the URL bar to match the filters parameter.

Uses ‘replace’ to save polluting the back history (I hate that so much!)

        this.$router.replace(
          { path: this.$route.path, query: this.filters },
          e => {}
        )